home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / imap-3.0 / ANSI / ms / ms.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-12  |  99.5 KB  |  3,708 lines

  1. /*
  2.  * Program:    MS - Mail System Distributed Client
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    28 November 1988
  13.  * Last Edited:    12 July 1993
  14.  *
  15.  * Copyright 1993 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. /* Version information */
  37.  
  38. static char *copyright = "\
  39.  MS user interface is:\n\
  40.   Copyright 1993 University of Washington\n\
  41.  Electronic Mail C-Client is:\n\
  42.   Copyright 1993 University of Washington\n\
  43.   Original version Copyright 1988 The Leland Stanford Junior University\n\
  44.  CCMD command interface is:\n\
  45.   Copyright 1986, 1987 Columbia University in the City of New York";
  46. static char *author = "Mark Crispin";
  47. static char *version = "7.91";
  48. static char *bug_mailbox = "MRC";
  49. static char *bug_host = "CAC.Washington.EDU";
  50. static char *hostlist[] = {    /* SMTP server host list */
  51.   "mailhost",
  52.   "localhost",
  53.   0
  54. };
  55. static char *newslist[] = {    /* News server host list */
  56.   "news",
  57.   "newshost",
  58.   "localhost",
  59.   0
  60. };
  61.  
  62. /* Parameter files */
  63.  
  64. #include <stdio.h>
  65. #include <ctype.h>
  66. #if unix
  67. #include <netdb.h>
  68. #include <signal.h>
  69. #endif
  70. #include <sys/types.h>
  71. #include "ccmd.h"
  72. #include "mail.h"
  73. #include "osdep.h"
  74. #include "smtp.h"
  75. #include "nntp.h"
  76. #include "rfc822.h"
  77. #include "misc.h"
  78.  
  79.  
  80. /* Drivers we use */
  81.  
  82. extern DRIVER imapdriver,bezerkdriver,tenexdriver,mboxdriver,mhdriver,
  83.  newsdriver,nntpdriver,dummydriver,dawzdriver;
  84.  
  85.  
  86. /* Size of temporary buffers */
  87. #define TMPLEN 1024
  88.  
  89.  
  90. /* CCMD buffer sizes */
  91. #define CMDBFL 16384
  92. #define MAXMAILBOXES 1000
  93.  
  94. /* Header sizes */
  95. #define FROMLEN 20
  96. #define SUBJECTLEN 25
  97.  
  98. /* Function prototypes */
  99.  
  100. void main (int argc,char *argv[]);
  101. void ctrlc ();
  102. short ms_init ();
  103. void toplevel (int argc,char *argv[]);
  104. void do_cmd (void (*f) (short help));
  105. void do_help (void (*f) (short help));
  106. void c_answer (short help);
  107. void c_bboard (short help);
  108. void c_blank (short help);
  109. void c_bug (short help);
  110. void c_check (short help);
  111. void c_copy (short help);
  112. void c_create (short help);
  113. void c_daytime (short help);
  114. void c_debug (short help);
  115. void c_delete (short help);
  116. void c_echo (short help);
  117. void c_exit (short help);
  118. void c_expunge (short help);
  119. void c_find (short help);
  120. void c_flag (short help);
  121. void c_forward (short help);
  122. void c_get (short help);
  123. void c_headers (short help);
  124. void c_help (short help);
  125. void c_keyword (short help);
  126. void c_kill (short help);
  127. void c_literal_type (short help);
  128. void c_mark (short help);
  129. void c_move (short help);
  130. void c_next (short help);
  131. void c_post (short help);
  132. void c_previous (short help);
  133. void c_read (short help);
  134. void c_remail (short help);
  135. void c_remove (short help);
  136. void c_rename (short help);
  137. void c_quit (short help);
  138. void c_send (short help);
  139. void c_status (short help);
  140. void c_subscribe (short help);
  141. void c_take (short help);
  142. void takelevel ();
  143. void c_type (short help);
  144. void c_unanswer (short help);
  145. void c_undelete (short help);
  146. void c_unflag (short help);
  147. void c_unkeyword (short help);
  148. void c_unmark (short help);
  149. void c_unsubscribe (short help);
  150. void c_version (short help);
  151. void r_answer (short help);
  152. void r_copy (short help);
  153. void r_delete (short help);
  154. void r_flag (short help);
  155. void r_forward (short help);
  156. void r_headers (short help);
  157. void r_help (short help);
  158. void r_keyword (short help);
  159. void r_kill (short help);
  160. void r_literal_type (short help);
  161. void r_mark (short help);
  162. void r_move (short help);
  163. void r_next (short help);
  164. void r_previous (short help);
  165. void r_quit (short help);
  166. void r_remail (short help);
  167. void r_status (short help);
  168. void r_type (short help);
  169. void r_unanswer (short help);
  170. void r_undelete (short help);
  171. void r_unflag (short help);
  172. void r_unkeyword (short help);
  173. void r_unmark (short help);
  174. void send_level (ENVELOPE *msg,BODY *body);
  175. void do_scmd (int (*f)(short help,ENVELOPE *msg,BODY *body),
  176.           ENVELOPE *msg,BODY *body);
  177. void do_shelp (int (*f)(short help,ENVELOPE *msg,BODY *body),
  178.            ENVELOPE *msg,BODY *body);
  179. void s_bboards (short help,ENVELOPE *msg,BODY *body);
  180. void s_bcc (short help,ENVELOPE *msg,BODY *body);
  181. void s_cc (short help,ENVELOPE *msg,BODY *body);
  182. void s_display (short help,ENVELOPE *msg,BODY *body);
  183. void s_erase (short help,ENVELOPE *msg,BODY *body);
  184. void s_help (short help,ENVELOPE *msg,BODY *body);
  185. void s_quit (short help,ENVELOPE *msg,BODY *body);
  186. void s_remove (short help,ENVELOPE *msg,BODY *body);
  187. void s_send (short help,ENVELOPE *msg,BODY *body);
  188. void s_subject (short help,ENVELOPE *msg,BODY *body);
  189. void s_to (short help,ENVELOPE *msg,BODY *body);
  190. char do_sequence (char *def);
  191. void more (void (*f)(FILE *pipe,long arg),long arg);
  192. void do_display (FILE *pipe,long i);
  193. void do_header (FILE *pipe,long i);
  194. void do_get (char *mailbox);
  195. void do_status (MAILSTREAM *stream);
  196. void do_version ();
  197. void answer_message (int msgno,int allflag);
  198. void header_message (FILE *file,long msgno);
  199. void literal_type_message (FILE *file,long msgno);
  200. void remail_message (int msgno,ADDRESS *adr);
  201. void type_message (FILE *file,long msgno);
  202. void type_address (FILE *file,char *tag,ADDRESS *adr);
  203. void type_string (FILE *file,int *pos,char *str1,char chr,char *str2);
  204. ENVELOPE *send_init ();
  205. ADDRESS *copy_adr (ADDRESS *adr,ADDRESS *ret);
  206. ADDRESS *remove_adr (ADDRESS *adr,char *text);
  207. BODY *send_text ();
  208. void send_message (ENVELOPE *msg,BODY *body);
  209. int onoff ();
  210. void cmerr (char *string);
  211.  
  212. /* Global variables */
  213.  
  214. static char sequence[CMDBFL];    /* string representing sequence */
  215. static long current = 0;    /* current message for NEXT, PREVIOUS, KILL */
  216. static char cmdbuf[CMDBFL];    /* command buffer */
  217. static char atmbuf[CMDBFL];    /* atom buffer */
  218. static char wrkbuf[CMDBFL];    /* work buffer */
  219. static keywrd mbx[MAXMAILBOXES];/* mailbox list */
  220. static keytab mbxtab = {0,mbx};    /* pointer to mailbox list */
  221. static keywrd bbd[MAXMAILBOXES];/* mailbox list */
  222. static keytab bbdtab = {0,bbd};    /* pointer to bboard list */
  223. static int critical = NIL;    /* in critical code flag */
  224. static int done = NIL;        /* exit flag */
  225. static int quitted = NIL;    /* quitted from send flag */
  226. static int debug = NIL;        /* debugging flag */
  227. static char *curhst = NIL;    /* current host */
  228. static char *curusr = NIL;    /* current login user */
  229. static char *personal = NIL;    /* user's personal name */
  230. static int nmsgs = 0;        /* local number of messages */
  231. static MAILSTREAM *stream = NIL;/* current mail stream */
  232. static char *fwdhdr = "\n ** Begin Forwarded Message(s) **\n\n";
  233. static keywrd flags[NUSERFLAGS];/* user flag table */
  234. static keytab flgtab = {0,flags};
  235.  
  236. /* Top level command table */
  237.  
  238. static keywrd cmds[] = {
  239.   {"ANSWER",    NIL,    (keyval) c_answer},
  240.   {"BBOARD",    NIL,    (keyval) c_bboard},
  241.   {"BLANK",    NIL,    (keyval) c_blank},
  242.   {"BUG",    NIL,    (keyval) c_bug},
  243.   {"CHECK",    NIL,    (keyval) c_check},
  244.   {"COPY",    NIL,    (keyval) c_copy},
  245.   {"CREATE",    NIL,    (keyval) c_create},
  246.   {"D",        KEY_INV|KEY_ABR,(keyval) 10},
  247.   {"DAYTIME",    NIL,    (keyval) c_daytime},
  248.   {"DEBUG",    NIL,    (keyval) c_debug},
  249.   {"DELETE",    NIL,    (keyval) c_delete},
  250.   {"ECHO",    NIL,    (keyval) c_echo},
  251.   {"EX",    KEY_INV|KEY_ABR,(keyval) 13},
  252.   {"EXIT",    NIL,    (keyval) c_exit},
  253.   {"EXPUNGE",    NIL,    (keyval) c_expunge},
  254.   {"FIND",    NIL,    (keyval) c_find},
  255.   {"FLAG",    NIL,    (keyval) c_flag},
  256.   {"FORWARD",    NIL,    (keyval) c_forward},
  257.   {"GET",    NIL,    (keyval) c_get},
  258.   {"H",        KEY_INV|KEY_ABR,(keyval) 20},
  259.   {"HEADERS",    NIL,    (keyval) c_headers},
  260.   {"HELP",    NIL,    (keyval) c_help},
  261.   {"KEYWORD",    NIL,    (keyval) c_keyword},
  262.   {"KILL",    NIL,    (keyval) c_kill},
  263.   {"LITERAL-TYPE",NIL,    (keyval) c_literal_type},
  264.   {"MARK",    NIL,    (keyval) c_mark},
  265.   {"MOVE",    NIL,    (keyval) c_move},
  266.   {"NEXT",    NIL,    (keyval) c_next},
  267.   {"POST",    NIL,    (keyval) c_post},
  268.   {"PREVIOUS",    NIL,    (keyval) c_previous},
  269.   {"QUIT",    NIL,    (keyval) c_quit},
  270.   {"R",        KEY_INV|KEY_ABR,(keyval) 32},
  271.   {"READ",    NIL,    (keyval) c_read},
  272.   {"REMAIL",    NIL,    (keyval) c_remail},
  273.   {"REMOVE",    NIL,    (keyval) c_remove},
  274.   {"RENAME",    NIL,    (keyval) c_rename},
  275.   {"S",        KEY_INV|KEY_ABR,(keyval) 37},
  276.   {"SEND",    NIL,    (keyval) c_send},
  277.   {"SUBSCRIBE",    NIL,    (keyval) c_subscribe},
  278.   {"STATUS",    NIL,    (keyval) c_status},
  279.   {"T",        KEY_INV|KEY_ABR,(keyval) 42},
  280.   {"TAKE",    NIL,    (keyval) c_take},
  281.   {"TYPE",    NIL,    (keyval) c_type},
  282.   {"UNANSWER",    NIL,    (keyval) c_unanswer},
  283.   {"UNDELETE",    NIL,    (keyval) c_undelete},
  284.   {"UNFLAG",    NIL,    (keyval) c_unflag},
  285.   {"UNKEYWORD",    NIL,    (keyval) c_unkeyword},
  286.   {"UNMARK",    NIL,    (keyval) c_unmark},
  287.   {"UNSUBSCRIBE",NIL,    (keyval) c_unsubscribe},
  288.   {"VERSION",    NIL,    (keyval) c_version},
  289. };
  290. static keytab cmdtab = {(sizeof (cmds)/sizeof (keywrd)),cmds};
  291.  
  292. /* Main program */
  293.  
  294. void main (int argc,char *argv[])
  295. {
  296. #if unix
  297.   signal (SIGINT,ctrlc);    /* set up CTRL/C handler */
  298.   signal (SIGQUIT,SIG_IGN);    /* ignore quit and pipe signals */
  299.   signal (SIGPIPE,SIG_IGN);
  300. #endif
  301.                 /* initialize CCMD */
  302.   cmbufs (cmdbuf,CMDBFL,atmbuf,CMDBFL,wrkbuf,CMDBFL);
  303.   cmseti (stdin,stdout,stderr);
  304.   if (ms_init () && argc <= 1) {/* initialize global state and if OK... */
  305.     cmcls ();            /* blank the screen */
  306.     do_version ();        /* display version */
  307.     do_get ("INBOX");        /* get INBOX */
  308.   }
  309.   toplevel (argc,argv);        /* enter top level cmd parser */
  310.   stream = mail_close (stream);    /* punt mail stream */
  311.   critical = T;            /* don't allow CTRL/C any more */
  312.   cmdone ();            /* restore the world */
  313.   exit (0);            /* all done */
  314. }
  315.  
  316.  
  317. #if unix
  318. /* Respond to CTRL/C
  319.  */
  320.  
  321. void ctrlc ()
  322. {
  323.   cmxprintf ("^C\n");        /* make sure user gets confirmation */
  324.   if (!critical) {        /* must not be critical */
  325.     if (!done) done = T;    /* if global done not set, set it now */
  326.     else {            /* else two in a row kill us */
  327.       cmdone ();        /* restore the world */
  328.       _exit (0);
  329.     }
  330.   }
  331. }
  332. #endif
  333.  
  334. /* Initialize MS global state
  335.  * Returns: T if OK, NIL if error
  336.  */
  337.  
  338. short ms_init ()
  339. {
  340. #if unix
  341.   char tmp[TMPLEN];
  342.   char *name;
  343.   char *suffix;
  344.   struct passwd *pwd;
  345.   struct hostent *host_name;
  346. #endif
  347.   if (curhst) fs_give ((void **) &curhst);
  348.   if (curusr) fs_give ((void **) &curusr);
  349. #if unix
  350.   gethostname (tmp,TMPLEN);    /* get local name */
  351.                 /* get it in full form */
  352.   curhst = (host_name = gethostbyname (tmp)) ?
  353.     cpystr (host_name->h_name) : cpystr (tmp);
  354.                 /* get user name and passwd entry */
  355.   if (name = (char *) getlogin ()) pwd = getpwnam (name);
  356.   else {
  357.     pwd = getpwuid (getuid ());    /* get it this way if detached, etc */
  358.     name = pwd->pw_name;
  359.   }
  360.   curusr = cpystr (name);    /* current user is this name */
  361.   if (!personal) {        /* this is OK to do only once */
  362.     strcpy (tmp,pwd->pw_gecos);    /* probably not necessay but be safe */
  363.                 /* dyke out the office and phone poop */
  364.     if (suffix = (char *) strchr (tmp,',')) suffix[0] = '\0';
  365.     personal = cpystr (tmp);    /* make a permanent copy of it */
  366.   }
  367. #else
  368.   curhst = cpystr ("somewhere");
  369.   curusr = cpystr ("unknown");
  370.   personal = NIL;
  371. #endif
  372.   flgtab._ktcnt = 0;        /* init keyword table */
  373.   mail_link (&imapdriver);    /* install the IMAP driver */
  374. #ifdef MSDOS
  375.   mail_link (&dawzdriver);    /* install the dawz mail driver */
  376. #endif
  377. #if unix
  378.   mail_link (&tenexdriver);    /* install the Tenex mail driver */
  379.   mail_link (&mhdriver);    /* link in mh mail driver */
  380.   mail_link (&mboxdriver);    /* link in mbox mail driver */
  381.   mail_link (&bezerkdriver);    /* install the Berkeley mail driver */
  382.   mail_link (&newsdriver);    /* install the news driver */
  383.   mail_link (&nntpdriver);    /* install the NNTP client driver */
  384.   mail_link (&dummydriver);    /* install dummy driver */
  385. #endif
  386.   mm_mailbox ("INBOX");        /* INBOX is always known!! */
  387.   mail_find (NIL,"*");        /* find local mailboxes */
  388.   mail_find_bboards (NIL,"*");    /* find local bboards */
  389.   return T;
  390. }
  391.  
  392. /* Top-level command parser
  393.  * Accepts: argument count
  394.  *        argument vector
  395.  */
  396.  
  397. void toplevel (int argc,char *argv[])
  398. {
  399.   int i;
  400.   pval parseval;
  401.   fdb *used;
  402.   static fdb cmdfdb = {_CMKEY,NIL,NIL,(pdat) &(cmdtab),"Command, ",NIL,NIL};
  403.   static fdb numfdb = {_CMNUM,CM_SDH,NIL,(pdat) 10,"Message number",NIL,NIL};
  404.   while (!done) {        /* until program wants to exit */
  405.     cmseter ();            /* set error trap */
  406.                 /* exit on EOF */
  407.     if (cmcsb._cmerr == CMxEOF) break;
  408.                 /* exit afterwards if command line argument */
  409.     if (cmargs (argc,argv)) done = T;
  410.     else prompt ("MS>");    /* prompt */
  411.     cmsetrp ();            /* set reparse trap */
  412.                 /* allow message number if have a stream */
  413.     cmdfdb._cmlst = stream ? (fdb *) &numfdb : NIL;
  414.                 /* parse command */
  415.     parse (&cmdfdb,&parseval,&used);
  416.     if (used == &numfdb) {    /* check for valid message number */
  417.       if (parseval._pvint >= 1 && parseval._pvint <= nmsgs) {
  418.     confirm ();        /* confirm single message */
  419.                 /* set up as current */
  420.     current = parseval._pvint;
  421.     for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  422.     mail_elt (stream,current)->spare = T;
  423.     sprintf (sequence,"%d",current);
  424.                 /* now output it */
  425.     more (type_message,current);
  426.       }
  427.       else cmerr ("Invalid message number");
  428.     }
  429.                 /* else do the command */
  430.     else do_cmd ((void *) parseval._pvint);
  431.   }
  432. }
  433.  
  434. /* Execute command
  435.  * Accepts: function
  436.  */
  437.  
  438. void do_cmd (void (*f) (short help))
  439. {
  440.   (*f)((short) NIL);        /* call function with help flag off */
  441. }
  442.  
  443.  
  444. /* Execute help for command
  445.  * Accepts: function
  446.  */
  447.  
  448. void do_help (void (*f) (short help))
  449. {
  450.   (*f)((short) T);        /* call function with help flag on */
  451. }
  452.  
  453. /* Top level commands */
  454.  
  455.  
  456. /* ANSWER command
  457.  * Accepts: help flag
  458.  */
  459.  
  460. static keywrd anscmds[] = {
  461.   {"ALL",    NIL,    (keyval) T},
  462.   {"SENDER-ONLY",NIL,    (keyval) NIL},
  463. };
  464. static keytab anstab = {(sizeof (anscmds)/sizeof (keywrd)),anscmds};
  465.  
  466. void c_answer (short help)
  467. {
  468.   if (help) cmxprintf ("\
  469. The ANSWER command composes and sends an answer to the specified messages.\n");
  470.   else {
  471.     char tmp[TMPLEN];
  472.     int i;
  473.     int msgno;
  474.     pval parseval;
  475.     fdb *used;
  476.     static fdb ansfdb = {_CMKEY,NIL,NIL,(pdat) &(anstab),"Answer option, ",
  477.                "SENDER-ONLY",NIL};
  478.     if (do_sequence (NIL)) for (msgno = 1; msgno <= nmsgs; ++msgno)
  479.       if (mail_elt (stream,msgno)->spare) {
  480.     current = msgno;    /* this is new current message */
  481.     cmseter ();        /* set error trap */
  482.     sprintf (tmp,"Send answer for message %d to: ",msgno);
  483.     prompt (tmp);        /* get reply option */
  484.     cmsetrp ();        /* set reparse trap */
  485.     parse (&ansfdb,&parseval,&used);
  486.     i = parseval._pvint;    /* save user's selection */
  487.     confirm ();
  488.     answer_message (msgno,i);
  489.       }
  490.   }
  491. }
  492.  
  493. /* BBOARD command
  494.  * Accepts: help flag
  495.  */
  496.  
  497. void c_bboard (short help)
  498. {
  499.   if (help) cmxprintf ("Establish connection to a bboard.\n");
  500.   else {
  501.     char tmp[TMPLEN];
  502.     pval parseval;
  503.     fdb *used;
  504.     static brktab mbxbrk = {
  505.       {                /* 1st char break array */
  506.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  507.     0x80,0x00,0x00,0x0b,0x80,0x00,0x00,0x0b
  508.       },
  509.       {                /* subsequent char break array */
  510.                 /* same as above, plus dots */
  511.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  512.     0x80,0x00,0x00,0x0b,0x80,0x00,0x00,0x0b
  513.       }
  514.     };
  515.     static fdb mb2fdb = {_CMFLD,CM_SDH,NIL,NIL,"new bboard name",NIL,&mbxbrk};
  516.     static fdb mbxfdb = {_CMKEY,NIL,&mb2fdb,(pdat)& (bbdtab),"known bboard, ",
  517.              NIL,&mbxbrk};
  518.                 /* default is general */
  519.     mbxfdb._cmdef =  "general";
  520.     noise ("NAME");
  521.                 /* parse the mailbox name */
  522.     parse (&mbxfdb,&parseval,&used);
  523.     sprintf (tmp,"*%s",atmbuf);
  524.     confirm ();
  525.     do_get (tmp);        /* get this bboard */
  526.   }
  527. }
  528.  
  529.  
  530. /* BLANK command
  531.  * Accepts: help flag
  532.  */
  533.  
  534. void c_blank (short help)
  535. {
  536.   if (help) cmxprintf ("Blanks the screen.\n");
  537.   else {
  538.     noise ("SCREEN");
  539.     confirm ();
  540.     cmcls ();            /* zap the screen */
  541.   }
  542. }
  543.  
  544. /* BUG command
  545.  * Accepts: help flag
  546.  */
  547.  
  548. void c_bug (short help)
  549. {
  550.   if (help) cmxprintf ("Report an MS bug.  This sends a message to %s@%s\n",
  551.                bug_mailbox,bug_host);
  552.   else {
  553.     char tmp[TMPLEN];
  554.     ENVELOPE *msg = NIL;
  555.     BODY *body = NIL;
  556.     pval parseval;
  557.     fdb *used;
  558.     static para_data pd = {NIL,NIL};
  559.     static fdb parafdb = {_CMPARA,NIL,NIL,NIL,NIL,NIL,NIL};
  560.     parafdb._cmdat = (pdat) &pd;
  561.     noise ("REPORT");
  562.     confirm ();
  563.     msg = send_init ();        /* make a message block */
  564.                 /* set up to-list */
  565.     sprintf (tmp,"MS maintainer <%s@%s>",bug_mailbox,bug_host);
  566.     rfc822_parse_adrlist (&msg->to,tmp,curhst);
  567.                 /* set up subject */
  568.     sprintf (tmp,"Bug in MS %s",version);
  569.     msg->subject = cpystr (tmp);
  570.     if (body = send_text ()) {    /* get text and send message */
  571.       send_level (msg,body);
  572.       mail_free_body (&body);
  573.     }
  574.     mail_free_envelope (&msg);    /* flush the message */
  575.   }
  576. }
  577.  
  578.  
  579. /* CHECK command
  580.  * Accepts: help flag
  581.  */
  582.  
  583. void c_check (short help)
  584. {
  585.   int lnmsgs;
  586.   if (help) cmxprintf ("\
  587. The CHECK command checks to see if any new messages have arrived in the\n\
  588. current mailbox.\n");
  589.   else {
  590.     if (stream) {
  591.       noise ("FOR NEW MESSAGES");
  592.       confirm ();
  593.       lnmsgs = nmsgs;        /* remember old value */
  594.       mail_check (stream);    /* check for new messages */
  595.                 /* report no change */
  596.       if (lnmsgs == nmsgs) cmxprintf ("There are no new messages\n");
  597.     }
  598.     else cmerr ("No mailbox is currently open");
  599.   }
  600. }
  601.  
  602. /* COPY command
  603.  * Accepts: help flag
  604.  */
  605.  
  606. void c_copy (short help)
  607. {
  608.   if (help) cmxprintf ("\
  609. The COPY command copies the specified messages into the specified mailbox.\n");
  610.   else {
  611.     char tmp[TMPLEN];
  612.     pval parseval;
  613.     fdb *used;
  614.     static brktab mbxbrk = {
  615.       {                /* 1st char break array */
  616.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  617.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1b
  618.       },
  619.       {                /* subsequent char break array */
  620.                 /* same as above, plus dots */
  621.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  622.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1f
  623.       }
  624.     };
  625.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox on this server",
  626.                "INBOX",&mbxbrk};
  627.     if (stream) {
  628.       noise ("TO MAILBOX");
  629.                 /* parse the mailbox name */
  630.       parse (&mbxfdb,&parseval,&used);
  631.       strcpy (tmp,atmbuf);    /* note the mailbox name */
  632.       if (do_sequence (NIL)) mail_copy (stream,sequence,tmp);
  633.     }
  634.     else cmerr ("No mailbox is currently open");
  635.   }
  636. }
  637.  
  638. /* CREATE command
  639.  * Accepts: help flag
  640.  */
  641.  
  642. void c_create (short help)
  643. {
  644.   if (help) cmxprintf ("Creates a new mailbox.\n");
  645.   else {
  646.     char tmp[TMPLEN];
  647.     pval parseval;
  648.     fdb *used;
  649.     static brktab mbxbrk = {
  650.       {                /* 1st char break array */
  651.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  652.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  653.       },
  654.       {                /* subsequent char break array */
  655.                 /* same as above, plus dots */
  656.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  657.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  658.       }
  659.     };
  660.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox","INBOX",&mbxbrk};
  661.     noise ("NEW MAILBOX NAMED");
  662.                 /* parse the mailbox name */
  663.     parse (&mbxfdb,&parseval,&used);
  664.     strcpy (tmp,atmbuf);    /* note the mailbox name */
  665.     confirm ();
  666.     mail_create (mail_open (NIL,(tmp[0] == '{') ? tmp : "INBOX",OP_PROTOTYPE),
  667.          tmp);
  668.   }
  669. }
  670.  
  671.  
  672. /* DAYTIME command
  673.  * Accepts: help flag
  674.  */
  675.  
  676. void c_daytime (short help)
  677. {
  678.   if (help) cmxprintf ("Types the current date and time.\n");
  679.   else {
  680.     char tmp[TMPLEN];
  681.     confirm ();
  682.     rfc822_date (tmp);
  683.     cmxprintf (" %s\n",tmp);
  684.   }
  685. }
  686.  
  687. /* DEBUG command
  688.  * Accepts: help flag
  689.  */
  690.  
  691. void c_debug (short help)
  692. {
  693.   int state;
  694.   if (help) cmxprintf ("\
  695. The DEBUG command enables debugging information, e.g. protocol telemetry.\n");
  696.   else {
  697.     noise ("PROTOCOL");
  698.     state = onoff ();
  699.     confirm ();
  700.     debug = state;        /* set debugging */
  701.     if (stream) {        /* and frob the stream if one's there */
  702.       if (debug) mail_debug (stream);
  703.       else mail_nodebug (stream);
  704.     }
  705.   }
  706. }
  707.  
  708.  
  709. /* DELETE command
  710.  * Accepts: help flag
  711.  */
  712.  
  713. void c_delete (short help)
  714. {
  715.   if (help) cmxprintf ("\
  716. The DELETE command makes the specified messages be deleted (marked for\n\
  717. removal by a subsequent EXIT or EXPUNGE command).\n");
  718.   else if (do_sequence (NIL)) mail_setflag (stream,sequence,"\\Deleted");
  719. }
  720.  
  721.  
  722. /* ECHO command
  723.  * Accepts: help flag
  724.  */
  725.  
  726. void c_echo (short help)
  727. {
  728.   if (help) cmxprintf ("\
  729. The ECHO command takes a text line as an argument and outputs it to the\n\
  730. terminal.  This is useful in TAKE files.\n");
  731.   else {
  732.     pval parseval;
  733.     fdb *used;
  734.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"text to echo back",NIL,NIL};
  735.     parse (&linfdb,&parseval,&used);
  736.     confirm ();
  737.     cmxprintf ("%s\n",atmbuf);
  738.   }
  739. }
  740.  
  741. /* EXIT command
  742.  * Accepts: help flag
  743.  */
  744.  
  745. void c_exit (short help)
  746. {
  747.   if (help) cmxprintf ("\
  748. The EXIT command expunges the current mailbox, closes the mailbox, and\n\
  749. exits this program.\n");
  750.   else {
  751.     noise ("PROGRAM");
  752.     confirm ();
  753.                 /* expunge if a stream is open */
  754.     if (stream) mail_expunge (stream);
  755.     done = T;            /* let top level know it's time to die */
  756.   }
  757. }
  758.  
  759.  
  760. /* EXPUNGE command
  761.  * Accepts: help flag
  762.  */
  763.  
  764. void c_expunge (short help)
  765. {
  766.   int i;
  767.   if (help) cmxprintf ("\
  768. The EXPUNGE command expunges (permanently removes all deleted messages from)\n\
  769. the current mailbox.\n");
  770.   else {
  771.     if (stream) {
  772.       noise ("MAILBOX");
  773.       confirm ();
  774.       mail_expunge (stream);    /* smash the deleted messages */
  775.                 /* invalidate the current sequence */
  776.       for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  777.       sequence[current = 0] = '\0';
  778.     }
  779.     else cmerr ("No mailbox is currently open");
  780.   }
  781. }
  782.  
  783. /* FIND command
  784.  * Accepts: help flag
  785.  */
  786.  
  787. static keywrd findcmds[] = {
  788.   {"FIRST",    NIL,    (keyval) NIL},
  789.   {"NEXT",    NIL,    (keyval) T}
  790. };
  791. static keytab findtab = {(sizeof (findcmds)/sizeof (keywrd)),findcmds};
  792.  
  793.  
  794. void c_find (short help)
  795. {
  796.   if (help) cmxprintf ("Find a bboard with recent mail.\n");
  797.   else {
  798.     int i;
  799.     char tmp[TMPLEN];
  800.     char *s,lsthst[TMPLEN];
  801.     pval parseval;
  802.     fdb *used;
  803.     static fdb findfdb = {_CMKEY,NIL,NIL,(pdat) &(findtab),"what to find, ",
  804.                 NIL,NIL};
  805.     findfdb._cmdef = (stream && *stream->mailbox == '*') ? "NEXT" : "FIRST";
  806.     parse (&findfdb,&parseval,&used);
  807.     i = parseval._pvint;    /* save user's selection */
  808.     noise ("BBOARD WITH NEW MAIL");
  809.     confirm ();
  810.                 /* start after current if NEXT specified */
  811.     if (i && stream && *stream->mailbox == '*')
  812.       for (i = 0; i < bbdtab._ktcnt;)
  813.     if (!strcmp (stream->mailbox + 1,bbd[i++]._kwkwd)) break;
  814.     if (stream && (s = (*stream->mailbox == '{') ? stream->mailbox :
  815.            (((*stream->mailbox == '*')&&(stream->mailbox[1] == '{')) ?
  816.             stream->mailbox + 1 : NIL))) {
  817.       strcpy (lsthst,s);    /* copy last host */
  818.       if (s = strchr (lsthst,'}')) s[1] = '\0';
  819.     }
  820.     else lsthst[0] = '\0';    /* no last host */
  821.     sequence[current = 0] = '\0';
  822.     flgtab._ktcnt = 0;        /* re-init keyword table */
  823.     while (i < bbdtab._ktcnt) {    /* try this bboard */
  824.       nmsgs = 0;        /* re-init number of messages */
  825.       sprintf (tmp,"*%s",bbd[i++]._kwkwd);
  826.                 /* specifies silence from driver... */
  827.       if ((stream = mail_open (stream,tmp,OP_SILENT)) && stream->recent) {
  828.     nmsgs = stream->nmsgs;    /* silence option doesn't call mm_exists */
  829.     stream->silent = NIL;    /* silent no more */
  830.     do_status (stream);    /* report status of mailbox */
  831.     for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  832.                 /* copy keywords to our table */
  833.     for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i) {
  834.                 /* value and keyword are the same */
  835.       flags[i]._kwval = (keyval) (flags[i]._kwkwd = stream->user_flags[i]);
  836.       flags[i]._kwflg = NIL;/* no special flags */
  837.     }
  838.     flgtab._ktcnt = i;    /* update keyword count */
  839.     if (*stream->mailbox == '{' || ((*stream->mailbox == '*') &&
  840.                     (stream->mailbox[1] == '{'))) {
  841.                 /* avoid duplicating find if same host */
  842.       strcpy (tmp,strchr (stream->mailbox,'{'));
  843.       if (s = strchr (tmp,'}')) s[1] = '\0';
  844.       if (strcmp (tmp,lsthst)) {
  845.         sprintf (lsthst,"%s*",tmp);
  846.         mail_find (stream,lsthst);
  847.         mail_find_bboards (stream,lsthst);
  848.       }
  849.     }
  850.     return;            /* all done */
  851.       }
  852.                 /* do a find if about to fall off the end */
  853.       else if (stream && (i == bbdtab._ktcnt)) {
  854.     if (stream->mailbox[1] == '{') {
  855.       strcpy (tmp,stream->mailbox + 1);
  856.       if (s = strchr (tmp,'}')) s[1] = '\0';
  857.       strcat (tmp,"*");
  858.     }
  859.     else strcpy (tmp,"*");
  860.     mail_find_bboards (stream,tmp);
  861.       }
  862.     }
  863.                 /* punt stream */
  864.     stream = mail_close (stream);
  865.     cmxprintf ("%%No more BBoards with new mail\n");
  866.   }
  867. }
  868.  
  869. /* FLAG command
  870.  * Accepts: help flag
  871.  */
  872.  
  873. void c_flag (short help)
  874. {
  875.   if (help) cmxprintf ("\
  876. The FLAG command makes the specified messages be flagged as urgent.\n");
  877.   else if (do_sequence (NIL)) mail_setflag (stream,sequence,"\\Flagged");
  878. }
  879.  
  880. /* FORWARD command
  881.  * Accepts: help flag
  882.  */
  883.  
  884. void c_forward (short help)
  885. {
  886.   if (help) cmxprintf ("\
  887. Forwards the specified messages with optional comments to another mailbox.\n");
  888.   else {
  889.     char tmp[TMPLEN];
  890.     int i,j;
  891.     int msgno;
  892.     char *text;
  893.     char *s;
  894.     ENVELOPE *msg;
  895.     BODY *body;
  896.     pval parseval;
  897.     fdb *used;
  898.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  899.     ADDRESS *adr = NIL;
  900.     linfdb._cmhlp = "list of forward recipients in RFC 822 format";
  901.     if (do_sequence (NIL)) {
  902.       while (!adr) {
  903.     cmseter ();        /* set error trap */
  904.     prompt ("To: ");    /* get recipient list */
  905.     cmsetrp ();        /* set reparse trap */
  906.     parse (&linfdb,&parseval,&used);
  907.                 /* parse recipient */
  908.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  909.     confirm ();
  910.       }
  911.       msg = send_init ();    /* get message block */
  912.       msg->to = adr;        /* set to-list */
  913.       if (body = send_text ()) {/* get initial text of comments */
  914.     fs_resize ((void **) &body->contents.text,
  915.            (i = strlen ((char *) body->contents.text)) +
  916.            (j = strlen (fwdhdr)));
  917.                 /* append the forward header */
  918.     memcpy (body->contents.text + i,fwdhdr,j);
  919.     for (msgno = 1; msgno <= nmsgs; ++msgno)
  920.       if (mail_elt (stream,msgno)->spare) {
  921.         if (!msg->subject) {/* if no subject yet */
  922.           tmp[0] = '[';    /* build string */
  923.                 /* get short from sans trailing spaces */
  924.           mail_fetchfrom (tmp+1,stream,msgno,FROMLEN);
  925.           for (s = tmp+FROMLEN; *s == ' '; --s) *s = '\0';
  926.           *++s = ':'; *++s = ' ';
  927.           strcpy (++s,(mail_fetchstructure (stream,msgno,NIL))->subject);
  928.           msg->subject = cpystr (tmp);
  929.         }
  930.         i += j;        /* current text size */
  931.                 /* get header of message */
  932.         text = mail_fetchheader (stream,msgno);
  933.                 /* resize the forward text */
  934.         fs_resize ((void **) &body->contents.text,i + (j = strlen (text)));
  935.                 /* append the forward message text */
  936.         memcpy (body->contents.text+i,text,j);
  937.         i += j;        /* current text size */
  938.                 /* get text of message */
  939.         text = mail_fetchtext (stream,msgno);
  940.                 /* resize the forward text */
  941.         fs_resize ((void **) &body->contents.text,i + (j = strlen (text)));
  942.                 /* append the forward message text */
  943.         memcpy (body->contents.text+i,text,j);
  944.       }
  945.     send_level (msg,body);    /* enter send-level */
  946.     mail_free_body (&body);
  947.       }
  948.       mail_free_envelope (&msg);/* flush the message */
  949.     }
  950.   }
  951. }
  952.  
  953. /* GET command
  954.  * Accepts: help flag
  955.  */
  956.  
  957. void c_get (short help)
  958. {
  959.   if (help) cmxprintf ("Establish connection to a mailbox.\n");
  960.   else {
  961.     char tmp[TMPLEN];
  962.     pval parseval;
  963.     fdb *used;
  964.     static brktab mbxbrk = {
  965.       {                /* 1st char break array */
  966.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  967.     0x80,0x00,0x00,0x0b,0x80,0x00,0x00,0x0b
  968.       },
  969.       {                /* subsequent char break array */
  970.                 /* same as above, plus dots */
  971.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  972.     0x80,0x00,0x00,0x0b,0x80,0x00,0x00,0x0b
  973.       }
  974.     };
  975.     static fdb mb2fdb = {_CMFLD,CM_SDH,NIL,NIL,"new mailbox name",NIL,&mbxbrk};
  976.     static fdb mbxfdb = {_CMKEY,NIL,&mb2fdb,(pdat)& (mbxtab),"known mailbox, ",
  977.              NIL,&mbxbrk};
  978.                 /* default is current mailbox or INBOX */
  979.     mbxfdb._cmdef = stream ? stream->mailbox : "INBOX";
  980.     noise ("MAILBOX");
  981.                 /* parse the mailbox name */
  982.     parse (&mbxfdb,&parseval,&used);
  983.     strcpy (tmp,atmbuf);
  984.     confirm ();
  985.     do_get (tmp);        /* get this mailbox */
  986.   }
  987. }
  988.  
  989. /* HEADERS command
  990.  * Accepts: help flag
  991.  */
  992.  
  993. void c_headers (short help)
  994. {
  995.   if (help) cmxprintf ("\
  996. The HEADERS command displays one-line summaries of the specified messages.\n");
  997.   else {
  998.                 /* parse sequence */
  999.     if (!do_sequence (NIL)) return;
  1000.     more (do_header,NIL);
  1001.   }
  1002. }
  1003.  
  1004.  
  1005. /* HELP command
  1006.  * Accepts: help flag
  1007.  */
  1008.  
  1009. void c_help (short help)
  1010. {
  1011.   static fdb cmdfdb = {_CMKEY,NIL,NIL,(pdat) &(cmdtab),"Command, ",NIL,NIL};
  1012.   static fdb hlpfdb = {_CMCFM,NIL,&cmdfdb,NIL,NIL,NIL,NIL};
  1013.   pval parseval;
  1014.   fdb *used;
  1015.   if (help) cmxprintf ("\
  1016. The HELP command gives short descriptions of the MS commands.\n");
  1017.   else {
  1018.     noise ("WITH");
  1019.                 /* parse a command */
  1020.     parse (&hlpfdb,&parseval,&used);
  1021.     if (used == &hlpfdb) cmxprintf ("\
  1022. MS is a distributed electronic mail client using the Interactive Mail\n\
  1023. Access Protocol described in RFC-1176.  Its user interface is for the most\n\
  1024. part a subset of MM.  Please refer to the MM documentation on your system\n\
  1025. for more information.\n");
  1026.     else {
  1027.       void *which = (void *) parseval._pvint;
  1028.       confirm ();
  1029.       do_help (which);        /* dispatch to appropriate command */
  1030.     }
  1031.   }
  1032. }
  1033.  
  1034. /* KEYWORD command
  1035.  * Accepts: help flag
  1036.  */
  1037.  
  1038. void c_keyword (short help)
  1039. {
  1040.   if (help) cmxprintf ("\
  1041. The KEYWORD command makes the specified messages have the specified\n\
  1042. keyword.\n");
  1043.   else {
  1044.     char tmp[TMPLEN];
  1045.     pval parseval;
  1046.     fdb *used;
  1047.     static fdb flgfdb = {_CMKEY,NIL,NIL,(pdat) &(flgtab),"Keyword, ",NIL,NIL};
  1048.     if (stream) {
  1049.       noise ("NAME");
  1050.                 /* parse the keyword */
  1051.       parse (&flgfdb,&parseval,&used);
  1052.       strcpy (tmp,(char *) parseval._pvint);
  1053.       if (do_sequence (NIL)) mail_setflag (stream,sequence,tmp);
  1054.     }
  1055.     else cmerr ("No mailbox is currently open");
  1056.   }
  1057. }
  1058.  
  1059.  
  1060. /* KILL command
  1061.  * Accepts: help flag
  1062.  */
  1063.  
  1064. void c_kill (short help)
  1065. {
  1066.   int i;
  1067.   if (help) cmxprintf ("\
  1068. The KILL command deletes the current message and does an implicit NEXT.\n");
  1069.   else {
  1070.     if (!stream) cmerr ("No mailbox is currently open");
  1071.     else {
  1072.       if (!current) cmerr ("No current message");
  1073.       else {
  1074.     char tmp[TMPLEN];
  1075.     noise ("MESSAGE");
  1076.     confirm ();
  1077.                 /* delete the current message */
  1078.     sprintf (tmp,"%d",current);
  1079.     mail_setflag (stream,tmp,"\\Deleted");
  1080.     if (current >= nmsgs) cmxprintf ("%%No next message\n");
  1081.     else {            /* invalidate the current sequence */
  1082.       for (i=1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  1083.                 /* select next message and type it */
  1084.       mail_elt (stream,++current)->spare = T;
  1085.       sprintf (sequence,"%d",current);
  1086.       more (type_message,current);
  1087.     }
  1088.       }
  1089.     }
  1090.   }
  1091. }
  1092.  
  1093. /* LITERAL-TYPE command
  1094.  * Accepts: help flag
  1095.  */
  1096.  
  1097. void c_literal_type (short help)
  1098. {
  1099.   if (help) cmxprintf ("\
  1100. The LITERAL-TYPE command types the specified messages in original format.\n");
  1101.   else {
  1102.     int i;
  1103.     if (!do_sequence (NIL)) return;
  1104.                 /* type messages */
  1105.     for (i = 1; i <= nmsgs; ++i) if (mail_elt (stream,i)->spare)
  1106.       more (literal_type_message,i);
  1107.   }
  1108. }
  1109.  
  1110.  
  1111. /* MARK command
  1112.  * Accepts: help flag
  1113.  */
  1114.  
  1115. void c_mark (short help)
  1116. {
  1117.   if (help) cmxprintf ("\
  1118. The MARK command makes the specified messages be marked as seen.\n");
  1119.   else if (do_sequence (NIL)) mail_setflag (stream,sequence,"\\Seen");
  1120. }
  1121.  
  1122. /* MOVE command
  1123.  * Accepts: help flag
  1124.  */
  1125.  
  1126. void c_move (short help)
  1127. {
  1128.   if (help) cmxprintf ("\
  1129. The MOVE command moves the specified messages into the specified mailbox\n\
  1130. and then deletes them from this mailbox.\n");
  1131.   else {
  1132.     char tmp[TMPLEN];
  1133.     pval parseval;
  1134.     fdb *used;
  1135.     static brktab mbxbrk = {
  1136.       {                /* 1st char break array */
  1137.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  1138.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1b
  1139.       },
  1140.       {                /* subsequent char break array */
  1141.                 /* same as above, plus dots */
  1142.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  1143.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1f
  1144.       }
  1145.     };
  1146.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox on this server",
  1147.                "INBOX",&mbxbrk};
  1148.     if (stream) {
  1149.       noise ("TO MAILBOX");
  1150.                 /* parse the mailbox name */
  1151.       parse (&mbxfdb,&parseval,&used);
  1152.       strcpy (tmp,atmbuf);    /* note the mailbox name */
  1153.       if (do_sequence (NIL)) mail_move (stream,sequence,tmp);
  1154.     }
  1155.     else cmerr ("No mailbox is currently open");
  1156.   }
  1157. }
  1158.  
  1159. /* NEXT command
  1160.  * Accepts: help flag
  1161.  */
  1162.  
  1163. void c_next (short help)
  1164. {
  1165.   int i;
  1166.   if (help) cmxprintf ("\
  1167. The NEXT command types the next message in the mailbox.\n");
  1168.   else {
  1169.     if (!stream) cmerr ("No mailbox is currently open");
  1170.     else {
  1171.       if (!current) cmerr ("No current message");
  1172.       else {
  1173.     char tmp[TMPLEN];
  1174.     noise ("MESSAGE");
  1175.     confirm ();
  1176.     if (current >= nmsgs)
  1177.       cmxprintf (" Currently at end, message %d\n",current);
  1178.     else {
  1179.                 /* invalidate the current sequence */
  1180.       for (i=1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  1181.                 /* select next message */
  1182.       mail_elt (stream,++current)->spare = T;
  1183.       sprintf (sequence,"%d",current);
  1184.       more (type_message,current);
  1185.     }
  1186.       }
  1187.     }
  1188.   }
  1189. }
  1190.  
  1191. /* POST command
  1192.  * Accepts: help flag
  1193.  */
  1194.  
  1195. void c_post (short help)
  1196. {
  1197.   if (help) cmxprintf ("Compose and send a new BBoard message.\n");
  1198.   else {
  1199.     char tmp[TMPLEN];
  1200.     ENVELOPE *msg = NIL;
  1201.     BODY *body;
  1202.     pval parseval;
  1203.     fdb *used;
  1204.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  1205.     static fdb optfdb = {_CMCFM,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  1206.     static para_data pd = {NIL,NIL};
  1207.     static fdb parafdb = {_CMPARA,NIL,NIL,NIL,NIL,NIL,NIL};
  1208.     parafdb._cmdat = (pdat) &pd;
  1209.     noise ("TO BULLETIN BOARD(S)");
  1210.     linfdb._cmhlp = "list of bulletin boards";
  1211.     parse (&linfdb,&parseval,&used);
  1212.     if (atmbuf[0]) {        /* if specified recipient on command line */
  1213.       msg = send_init ();    /* get a message block and store to list */
  1214.       msg->newsgroups = cpystr (atmbuf);
  1215.     }
  1216.     confirm ();
  1217.                 /* get a message block now if not one yet */
  1218.     if (!msg) msg = send_init ();
  1219.     while (!msg->newsgroups) {    /* loop here until he gives a BBoard */
  1220.       prompt ("BBoard(s): ");    /* prompt for and parse BBoard-list */
  1221.       cmsetrp ();        /* set reparse trap */
  1222.       parse (&linfdb,&parseval,&used);
  1223.                 /* free old one in case reparse */
  1224.       if (msg->newsgroups) fs_give ((void **) &msg->newsgroups);
  1225.       msg->newsgroups = cpystr (atmbuf);
  1226.       confirm ();
  1227.     }
  1228.                 /* Subject is optional */
  1229.     linfdb._cmlst = (fdb *) &optfdb;
  1230.     prompt ("Subject: ");    /* prompt for and get Subject */
  1231.     cmsetrp ();            /* set reparse trap */
  1232.     linfdb._cmhlp = "single-line subject for this posting";
  1233.     parse (&linfdb,&parseval,&used);
  1234.                 /* free old one in case reparse */
  1235.     if (msg->subject) fs_give ((void **) &msg->subject);
  1236.     if (atmbuf[0]) msg->subject = cpystr (atmbuf);
  1237.     confirm ();
  1238.     if (body = send_text ()) {    /* get text and send message */
  1239.       send_level (msg,body);
  1240.       mail_free_body (&body);
  1241.     }
  1242.     mail_free_envelope (&msg);    /* flush the message */
  1243.   }
  1244. }
  1245.  
  1246. /* PREVIOUS command
  1247.  * Accepts: help flag
  1248.  */
  1249.  
  1250. void c_previous (short help)
  1251. {
  1252.   int i;
  1253.   if (help) cmxprintf ("\
  1254. The PREVIOUS command types the previous message in the mailbox.\n");
  1255.   else {
  1256.     char tmp[TMPLEN];
  1257.     if (!stream) cmerr ("No mailbox is currently open");
  1258.     else {
  1259.       if (!current) cmerr ("No current message");
  1260.       else {
  1261.     noise ("MESSAGE");
  1262.     confirm ();
  1263.     if (current == 1)
  1264.       cmxprintf (" Currently at beginning, message %d\n",current);
  1265.     else {
  1266.                 /* invalidate the current sequence */
  1267.       for (i=1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  1268.                 /* select previous message */
  1269.       mail_elt (stream,--current)->spare = T;
  1270.       sprintf (sequence,"%d",current);
  1271.       more (type_message,current);
  1272.     }
  1273.       }
  1274.     }
  1275.   }
  1276. }
  1277.  
  1278.  
  1279. /* QUIT command
  1280.  * Accepts: help flag
  1281.  */
  1282.  
  1283. void c_quit (short help)
  1284. {
  1285.   if (help) cmxprintf ("\
  1286. The QUIT command closes the mailbox and quits this program without\n\
  1287. expunging the mailbox.\n");
  1288.   else {
  1289.     noise ("PROGRAM");
  1290.     confirm ();
  1291.     done = T;            /* let top level know it's time to die */
  1292.   }
  1293. }
  1294.  
  1295. /* Read level command table */
  1296.  
  1297. static keywrd readcmds[] = {
  1298.   {"ANSWER",    KEY_INV,(keyval) r_answer},
  1299.   {"BLANK",    NIL,    (keyval) c_blank},
  1300.   {"BUG",    NIL,    (keyval) c_bug},
  1301.   {"COPY",    NIL,    (keyval) r_copy},
  1302.   {"D",        KEY_INV|KEY_ABR,(keyval) 7},
  1303.   {"DAYTIME",    NIL,    (keyval) c_daytime},
  1304.   {"DEBUG",    NIL,    (keyval) c_debug},
  1305.   {"DELETE",    NIL,    (keyval) r_delete},
  1306.   {"ECHO",    NIL,    (keyval) c_echo},
  1307.   {"FLAG",    NIL,    (keyval) r_flag},
  1308.   {"FORWARD",    NIL,    (keyval) r_forward},
  1309.   {"H",        KEY_INV|KEY_ABR,(keyval) 12},
  1310.   {"HEADER",    NIL,    (keyval) r_headers},
  1311.   {"HELP",    NIL,    (keyval) r_help},
  1312.   {"K",        KEY_INV|KEY_ABR,(keyval) 16},
  1313.   {"KEYWORD",    NIL,    (keyval) r_keyword},
  1314.   {"KILL",    NIL,    (keyval) r_kill},
  1315.   {"LITERAL-TYPE",NIL,    (keyval) r_literal_type},
  1316.   {"MARK",    NIL,    (keyval) r_mark},
  1317.   {"MOVE",    NIL,    (keyval) r_move},
  1318.   {"NEXT",    NIL,    (keyval) r_next},
  1319.   {"POST",    NIL,    (keyval) c_post},
  1320.   {"PREVIOUS",    NIL,    (keyval) r_previous},
  1321.   {"QUIT",    NIL,    (keyval) r_quit},
  1322.   {"R",        KEY_INV|KEY_ABR,(keyval) 26},
  1323.   {"REMAIL",    NIL,    (keyval) r_remail},
  1324.   {"REPLY",    NIL,    (keyval) r_answer},
  1325.   {"S",        KEY_INV|KEY_ABR,(keyval) 28},
  1326.   {"SEND",    NIL,    (keyval) c_send},
  1327.   {"STATUS",    NIL,    (keyval) r_status},
  1328.   {"TYPE",    NIL,    (keyval) r_type},
  1329.   {"UNANSWER",    NIL,    (keyval) r_unanswer},
  1330.   {"UNDELETE",    NIL,    (keyval) r_undelete},
  1331.   {"UNFLAG",    NIL,    (keyval) r_unflag},
  1332.   {"UNKEYWORD",    NIL,    (keyval) r_unkeyword},
  1333.   {"UNMARK",    NIL,    (keyval) r_unmark},
  1334.   {"VERSION",    NIL,    (keyval) c_version},
  1335. };
  1336. static keytab reatab = {(sizeof (readcmds)/sizeof (keywrd)),readcmds};
  1337.  
  1338. /* READ command
  1339.  * Accepts: help flag
  1340.  */
  1341.  
  1342. void c_read (short help)
  1343. {
  1344.   pval parseval;
  1345.   fdb *used;
  1346.   static fdb cmdfdb = {_CMKEY,NIL,NIL,(pdat) &(reatab),"Command, ","NEXT",NIL};
  1347.   char *defseq = (stream && *stream->mailbox == '*') ? "NEW" : "UNSEEN";
  1348.   int olddone = done;
  1349.   if (help) cmxprintf ("\
  1350. The READ command enters read sub-mode on the specified messages.\n");
  1351.   else if (do_sequence (defseq)) for (current=1; current <= nmsgs; ++current)
  1352.     if (mail_elt (stream,current)->spare) {
  1353.                 /* make sure we have flags & envelope */
  1354.       mail_fetchstructure (stream,current,NIL);
  1355.       if (mail_elt (stream,current)->deleted)
  1356.     cmxprintf (" Message %d deleted\n",current);
  1357.       else {
  1358.     cmcls ();        /* zap the screen */
  1359.     more (type_message,current);
  1360.       }
  1361.       done = NIL;        /* not done yet */
  1362.       while (!done) {
  1363.     cmseter ();        /* set error trap */
  1364.                 /* exit on EOF */
  1365.     if (cmcsb._cmerr == CMxEOF) break;
  1366.     prompt ("MS-Read>");    /* prompt */
  1367.     cmsetrp ();        /* set reparse trap */
  1368.                 /* parse command */
  1369.     parse (&cmdfdb,&parseval,&used);
  1370.     do_cmd ((void *) parseval._pvint);
  1371.       }
  1372.       if (done > 0) break;    /* negative means NEXT command */
  1373.     }
  1374.   sequence[current = 0] = '\0';    /* invalidate sequence */
  1375.   done = olddone;        /* cancel done status */
  1376. }
  1377.  
  1378. /* REMAIL command
  1379.  * Accepts: help flag
  1380.  */
  1381.  
  1382. void c_remail (short help)
  1383. {
  1384.   if (help) cmxprintf ("Remail the specified messages to another mailbox.\n");
  1385.   else {
  1386.     char tmp[TMPLEN];
  1387.     int msgno;
  1388.     pval parseval;
  1389.     fdb *used;
  1390.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  1391.     ADDRESS *adr = NIL;
  1392.     linfdb._cmhlp = "list of remail recipients in RFC 822 format";
  1393.     if (do_sequence (NIL)) {
  1394.       while (!adr) {
  1395.     cmseter ();        /* set error trap */
  1396.     prompt ("To: ");    /* get recipient list */
  1397.     cmsetrp ();        /* set reparse trap */
  1398.     parse (&linfdb,&parseval,&used);
  1399.                 /* parse recipient */
  1400.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  1401.     confirm ();
  1402.       }
  1403.       for (msgno = 1; msgno <= nmsgs; ++msgno)
  1404.     if (mail_elt (stream,msgno)->spare)
  1405.       remail_message ((current = msgno),adr);
  1406.       mail_free_address (&adr);    /* flush the address */
  1407.     }
  1408.   }
  1409. }
  1410.  
  1411. /* REMOVE command
  1412.  * Accepts: help flag
  1413.  */
  1414.  
  1415. void c_remove (short help)
  1416. {
  1417.   if (help) cmxprintf ("Removes an existing mailbox.\n");
  1418.   else {
  1419.     char tmp[TMPLEN];
  1420.     pval parseval;
  1421.     fdb *used;
  1422.     static brktab mbxbrk = {
  1423.       {                /* 1st char break array */
  1424.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  1425.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1426.       },
  1427.       {                /* subsequent char break array */
  1428.                 /* same as above, plus dots */
  1429.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  1430.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1431.       }
  1432.     };
  1433.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox",NIL,&mbxbrk};
  1434.     noise ("MAILBOX NAMED");
  1435.                 /* parse the mailbox name */
  1436.     parse (&mbxfdb,&parseval,&used);
  1437.     strcpy (tmp,atmbuf);    /* note the mailbox name */
  1438.     confirm ();
  1439.     mail_delete (NIL,tmp);
  1440.   }
  1441. }
  1442.  
  1443. /* RENAME command
  1444.  * Accepts: help flag
  1445.  */
  1446.  
  1447. void c_rename (short help)
  1448. {
  1449.   if (help) cmxprintf ("Renames an existing mailbox.\n");
  1450.   else {
  1451.     char tmp[TMPLEN],tmpx[TMPLEN];
  1452.     pval parseval;
  1453.     fdb *used;
  1454.     static brktab mbxbrk = {
  1455.       {                /* 1st char break array */
  1456.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  1457.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1458.       },
  1459.       {                /* subsequent char break array */
  1460.                 /* same as above, plus dots */
  1461.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  1462.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1463.       }
  1464.     };
  1465.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox",NIL,&mbxbrk};
  1466.     noise ("MAILBOX NAMED");
  1467.                 /* parse the mailbox name */
  1468.     parse (&mbxfdb,&parseval,&used);
  1469.     strcpy (tmp,atmbuf);    /* note the mailbox name */
  1470.     noise ("TO");
  1471.                 /* parse the mailbox name */
  1472.     parse (&mbxfdb,&parseval,&used);
  1473.     strcpy (tmpx,atmbuf);    /* note the mailbox name */
  1474.     confirm ();
  1475.     mail_rename (NIL,tmp,tmpx);
  1476.   }
  1477. }
  1478.  
  1479. /* SEND command
  1480.  * Accepts: help flag
  1481.  */
  1482.  
  1483. void c_send (short help)
  1484. {
  1485.   if (help) cmxprintf ("Compose and send a message.\n");
  1486.   else {
  1487.     char tmp[TMPLEN];
  1488.     ENVELOPE *msg = NIL;
  1489.     BODY *body;
  1490.     pval parseval;
  1491.     fdb *used;
  1492.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  1493.     static fdb optfdb = {_CMCFM,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  1494.     static para_data pd = {NIL,NIL};
  1495.     static fdb parafdb = {_CMPARA,NIL,NIL,NIL,NIL,NIL,NIL};
  1496.     parafdb._cmdat = (pdat) &pd;
  1497.     noise ("MESSAGE TO");
  1498.     linfdb._cmhlp = "list of primary recipients in RFC 822 format";
  1499.     parse (&linfdb,&parseval,&used);
  1500.     if (atmbuf[0]) {        /* if specified recipient on command line */
  1501.       msg = send_init ();    /* get a message block and store to list */
  1502.       rfc822_parse_adrlist (&msg->to,atmbuf,curhst);
  1503.     }
  1504.     confirm ();
  1505.                 /* get a message block now if not one yet */
  1506.     if (!msg) msg = send_init ();
  1507.     if (!msg->to) {        /* if no command line do hand-holding */
  1508.       while (!msg->to) {    /* loop here until he gives a To-list */
  1509.     prompt ("To: ");    /* prompt for and parse To-list */
  1510.     cmsetrp ();        /* set reparse trap */
  1511.     parse (&linfdb,&parseval,&used);
  1512.                 /* free old one in case reparse */
  1513.     if (msg->to) mail_free_address (&msg->to);
  1514.     rfc822_parse_adrlist (&msg->to,atmbuf,curhst);
  1515.     confirm ();
  1516.       }
  1517.                 /* cc and Subject are optional */
  1518.       linfdb._cmlst = (fdb *) &optfdb;
  1519.       prompt ("cc: ");        /* prompt for and parse cc-list */
  1520.       cmsetrp ();        /* set reparse trap */
  1521.       linfdb._cmhlp = "list of secondary recipients in RFC 822 format";
  1522.       parse (&linfdb,&parseval,&used);
  1523.                 /* free old one in case reparse */
  1524.       if (msg->cc) mail_free_address (&msg->cc);
  1525.       if (atmbuf[0]) rfc822_parse_adrlist (&msg->cc,atmbuf,curhst);
  1526.       confirm ();
  1527.     }
  1528.     prompt ("Subject: ");    /* prompt for and get Subject */
  1529.     cmsetrp ();            /* set reparse trap */
  1530.     linfdb._cmhlp = "single-line subject for this message";
  1531.     parse (&linfdb,&parseval,&used);
  1532.                 /* free old one in case reparse */
  1533.     if (msg->subject) fs_give ((void **) &msg->subject);
  1534.     if (atmbuf[0]) msg->subject = cpystr (atmbuf);
  1535.     confirm ();
  1536.     if (body = send_text ()) {    /* get text and send message */
  1537.       send_level (msg,body);
  1538.       mail_free_body (&body);
  1539.     }
  1540.     mail_free_envelope (&msg);    /* flush the message */
  1541.   }
  1542. }
  1543.  
  1544. /* STATUS command
  1545.  * Accepts: help flag
  1546.  */
  1547.  
  1548. void c_status (short help)
  1549. {
  1550.   if (help) cmxprintf ("Type status of current mailbox.\n");
  1551.   else {
  1552.     noise ("OF CURRENT MAILBOX");
  1553.     confirm ();
  1554.     if (stream) {
  1555.       do_status (stream);    /* output the status */
  1556.       if (sequence[0]) cmxprintf (" Current sequence: %s\n",sequence);
  1557.     }
  1558.     else cmxprintf ("%%No mailbox is currently open\n");
  1559.   }
  1560. }
  1561.  
  1562. static keywrd subcmds[] = {
  1563.   {"BBOARD",    NIL,    (keyval) NIL},
  1564.   {"MAILBOX",    NIL,    (keyval) T},
  1565. };
  1566. static keytab subtab = {(sizeof (subcmds)/sizeof (keywrd)),subcmds};
  1567.  
  1568.  
  1569. /* SUBSCRIBE command
  1570.  * Accepts: help flag
  1571.  */
  1572.  
  1573. void c_subscribe (short help)
  1574. {
  1575.   if (help) cmxprintf ("Subscribe to a mailbox or bboard.\n");
  1576.   else {
  1577.     int i;
  1578.     char tmp[TMPLEN];
  1579.     pval parseval;
  1580.     fdb *used;
  1581.     static brktab mbxbrk = {
  1582.       {                /* 1st char break array */
  1583.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  1584.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1585.       },
  1586.       {                /* subsequent char break array */
  1587.                 /* same as above, plus dots */
  1588.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  1589.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1590.       }
  1591.     };
  1592.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox",NIL,&mbxbrk};
  1593.     static fdb subfdb = {_CMKEY,NIL,NIL,(pdat) &(subtab),
  1594.                "subscription object, ","BBOARD",NIL};
  1595.     noise ("TO");
  1596.     parse (&subfdb,&parseval,&used);
  1597.     noise ((i = parseval._pvint) ? "MAILBOX NAMED" : "BBOARD NAMED");
  1598.                 /* parse the mailbox name */
  1599.     parse (&mbxfdb,&parseval,&used);
  1600.     strcpy (tmp,atmbuf);    /* note the mailbox name */
  1601.     confirm ();
  1602.     if (i) mail_subscribe (NIL,tmp);
  1603.     else mail_subscribe_bboard (NIL,tmp);
  1604.   }
  1605. }
  1606.  
  1607. /* TAKE command
  1608.  * Accepts: help flag
  1609.  */
  1610.  
  1611. void c_take (short help)
  1612. {
  1613.   if (help) cmxprintf ("Take commands from a file.\n");
  1614.   else cmtake (takelevel);
  1615. }
  1616.  
  1617.  
  1618. /* Routine called by TAKE to invoke top level */
  1619.  
  1620. void takelevel ()
  1621. {
  1622.   toplevel (NIL,NIL);        /* invoke top level */
  1623. }
  1624.  
  1625.  
  1626. /* TYPE command
  1627.  * Accepts: help flag
  1628.  */
  1629.  
  1630. void c_type (short help)
  1631. {
  1632.   if (help) cmxprintf ("The TYPE command types the specified messages.\n");
  1633.   else {
  1634.     int i;
  1635.     if (!do_sequence (NIL)) return;
  1636.     for (i = 1; i <= nmsgs; ++i) if (mail_elt (stream,i)->spare)
  1637.       more (type_message,i);
  1638.   }
  1639. }
  1640.  
  1641. /* UNANSWER command
  1642.  * Accepts: help flag
  1643.  */
  1644.  
  1645. void c_unanswer (short help)
  1646. {
  1647.   if (help) cmxprintf ("\
  1648. The UNANSWER command makes the specified messages not be answered.\n");
  1649.   else if (do_sequence (NIL)) mail_clearflag (stream,sequence,"\\Answered");
  1650. }
  1651.  
  1652.  
  1653. /* UNDELETE command
  1654.  * Accepts: help flag
  1655.  */
  1656.  
  1657. void c_undelete (short help)
  1658. {
  1659.   if (help) cmxprintf ("\
  1660. The UNDELETE command makes the specified messages not be deleted.\n");
  1661.   else if (do_sequence (NIL)) mail_clearflag (stream,sequence,"\\Deleted");
  1662. }
  1663.  
  1664.  
  1665. /* UNFLAG command
  1666.  * Accepts: help flag
  1667.  */
  1668.  
  1669. void c_unflag (short help)
  1670. {
  1671.   if (help) cmxprintf ("\
  1672. The UNFLAG command makes the specified messages not be flagged as urgent.\n");
  1673.   else if (do_sequence (NIL)) mail_clearflag (stream,sequence,"\\Flagged");
  1674. }
  1675.  
  1676. /* UNKEYWORD command
  1677.  * Accepts: help flag
  1678.  */
  1679.  
  1680. void c_unkeyword (short help)
  1681. {
  1682.   if (help) cmxprintf ("\
  1683. The UNKEYWORD command makes the specified messages not have the specified\n\
  1684. keyword.\n");
  1685.   else {
  1686.     char tmp[TMPLEN];
  1687.     pval parseval;
  1688.     fdb *used;
  1689.     static fdb flgfdb = {_CMKEY,NIL,NIL,(pdat) &(flgtab),"Keyword, ",NIL,NIL};
  1690.     if (stream) {
  1691.       noise ("NAME");
  1692.                 /* parse the keyword */
  1693.       parse (&flgfdb,&parseval,&used);
  1694.       strcpy (tmp,(char *) parseval._pvint);
  1695.       if (do_sequence (NIL)) mail_clearflag (stream,sequence,tmp);
  1696.     }
  1697.     else cmerr ("No mailbox is currently open");
  1698.   }
  1699. }
  1700.  
  1701.  
  1702. /* UNMARK command
  1703.  * Accepts: help flag
  1704.  */
  1705.  
  1706. void c_unmark (short help)
  1707. {
  1708.   if (help) cmxprintf ("\
  1709. The UNMARK command makes the specified messages not be marked as seen.\n");
  1710.   else if (do_sequence (NIL)) mail_clearflag (stream,sequence,"\\Seen");
  1711. }
  1712.  
  1713. /* UNSUBSCRIBE command
  1714.  * Accepts: help flag
  1715.  */
  1716.  
  1717. void c_unsubscribe (short help)
  1718. {
  1719.   if (help) cmxprintf ("Subscribe to a mailbox or bboard.\n");
  1720.   else {
  1721.     int i;
  1722.     char tmp[TMPLEN];
  1723.     pval parseval;
  1724.     fdb *used;
  1725.     static brktab mbxbrk = {
  1726.       {                /* 1st char break array */
  1727.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  1728.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1729.       },
  1730.       {                /* subsequent char break array */
  1731.                 /* same as above, plus dots */
  1732.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  1733.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x0b
  1734.       }
  1735.     };
  1736.     static fdb mb2fdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox name",NIL,&mbxbrk};
  1737.     static fdb mbxfdb = {_CMKEY,NIL,&mb2fdb,(pdat)& (mbxtab),"known mailbox, ",
  1738.              NIL,&mbxbrk};
  1739.     static fdb bb2fdb = {_CMFLD,CM_SDH,NIL,NIL,"bboard name",NIL,&mbxbrk};
  1740.     static fdb bbdfdb = {_CMKEY,NIL,&bb2fdb,(pdat)& (bbdtab),"known bboard, ",
  1741.              NIL,&mbxbrk};
  1742.     static fdb subfdb = {_CMKEY,NIL,NIL,(pdat) &(subtab),
  1743.                "subscription object, ","BBOARD",NIL};
  1744.     noise ("FROM");
  1745.     parse (&subfdb,&parseval,&used);
  1746.     noise ((i = parseval._pvint) ? "MAILBOX NAMED" : "BBOARD NAMED");
  1747.                 /* parse the mailbox name */
  1748.     parse (i ? &mbxfdb : &bbdfdb,&parseval,&used);
  1749.     strcpy (tmp,atmbuf);    /* note the mailbox name */
  1750.     confirm ();
  1751.     if (i) mail_unsubscribe (NIL,tmp);
  1752.     else mail_unsubscribe_bboard (NIL,tmp);
  1753.   }
  1754. }
  1755.  
  1756.  
  1757. /* VERSION command
  1758.  * Accepts: help flag
  1759.  */
  1760.  
  1761. void c_version (short help)
  1762. {
  1763.   if (help) cmxprintf ("Display the current version of this program.\n");
  1764.   else {
  1765.     noise ("OF MS");
  1766.     confirm ();
  1767.     do_version ();
  1768.     cmxprintf (" Written by %s\n%s\n",author,copyright);
  1769.   }
  1770. }
  1771.  
  1772. /* Read command level */
  1773.  
  1774.  
  1775. /* ANSWER command
  1776.  * Accepts: help flag
  1777.  */
  1778.  
  1779. void r_answer (short help)
  1780. {
  1781.   if (help) cmxprintf ("\
  1782. The REPLY command composes and sends an answer to this message.\n");
  1783.   else {
  1784.     int i;
  1785.     pval parseval;
  1786.     fdb *used;
  1787.     static fdb ansfdb = {_CMKEY,NIL,NIL,(pdat) &(anstab),"Answer option, ",
  1788.                "SENDER-ONLY",NIL};
  1789.     noise ("TO");        /* get reply option */
  1790.     parse (&ansfdb,&parseval,&used);
  1791.     i = parseval._pvint;    /* save user's selection */
  1792.     confirm ();
  1793.     answer_message (current,i);    /* do the answer and mark the message */
  1794.   }
  1795. }
  1796.  
  1797. /* COPY command
  1798.  * Accepts: help flag
  1799.  */
  1800.  
  1801. void r_copy (short help)
  1802. {
  1803.   if (help) cmxprintf ("\
  1804. The COPY command copies this message into the specified mailbox.\n");
  1805.   else {
  1806.     char tmp[TMPLEN];
  1807.     char copybox[TMPLEN];
  1808.     pval parseval;
  1809.     fdb *used;
  1810.     static brktab mbxbrk = {
  1811.       {                /* 1st char break array */
  1812.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  1813.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1b
  1814.       },
  1815.       {                /* subsequent char break array */
  1816.                 /* same as above, plus dots */
  1817.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  1818.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1f
  1819.       }
  1820.     };
  1821.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox on this server",
  1822.                "INBOX",&mbxbrk};
  1823.     noise ("TO MAILBOX");
  1824.                 /* parse the mailbox name */
  1825.     parse (&mbxfdb,&parseval,&used);
  1826.     strcpy (copybox,atmbuf);    /* note the mailbox name */
  1827.     confirm ();
  1828.     sprintf (tmp,"%d",current);
  1829.     mail_copy (stream,tmp,copybox);
  1830.   }
  1831. }
  1832.  
  1833. /* DELETE command
  1834.  * Accepts: help flag
  1835.  */
  1836.  
  1837. void r_delete (short help)
  1838. {
  1839.   if (help) cmxprintf ("\
  1840. The DELETE command makes this message be deleted (marked for\n\
  1841. removal by a subsequent EXIT or EXPUNGE command).\n");
  1842.   else {
  1843.     char tmp[TMPLEN];
  1844.     noise ("MESSAGE");
  1845.     confirm ();
  1846.     sprintf (tmp,"%d",current);
  1847.     mail_setflag (stream,tmp,"\\Deleted");
  1848.   }
  1849. }
  1850.  
  1851.  
  1852. /* FLAG command
  1853.  * Accepts: help flag
  1854.  */
  1855.  
  1856. void r_flag (short help)
  1857. {
  1858.   if (help) cmxprintf ("\
  1859. The FLAG command makes this message be flagged as urgent.\n");
  1860.   else {
  1861.     char tmp[TMPLEN];
  1862.     noise ("MESSAGE");
  1863.     confirm ();
  1864.     sprintf (tmp,"%d",current);
  1865.     mail_setflag (stream,tmp,"\\Flagged");
  1866.   }
  1867. }
  1868.  
  1869. /* FORWARD command
  1870.  * Accepts: help flag
  1871.  */
  1872.  
  1873. void r_forward (short help)
  1874. {
  1875.   if (help) cmxprintf ("\
  1876. Forwards this message with optional comments to another mailbox.\n");
  1877.   else {
  1878.     pval parseval;
  1879.     fdb *used;
  1880.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  1881.     char tmp[TMPLEN];
  1882.     int i,j,k;
  1883.     char *hdr,*text;
  1884.     char *s;
  1885.     ENVELOPE *msg;
  1886.     BODY *body;
  1887.     ADDRESS *adr = NIL;
  1888.     noise ("MESSAGE TO");
  1889.     linfdb._cmhlp = "list of forward recipients in RFC 822 format";
  1890.     parse (&linfdb,&parseval,&used);
  1891.                 /* parse recipient */
  1892.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  1893.     if (!adr) {
  1894.       cmerr ("No forward recipient specified");
  1895.       return;
  1896.     }
  1897.     confirm ();
  1898.     msg = send_init ();        /* get message block */
  1899.     msg->to = adr;        /* set to-list */
  1900.     tmp[0] = '[';        /* build string */
  1901.                 /* get short from sans trailing spaces */
  1902.     mail_fetchfrom (tmp+1,stream,current,FROMLEN);
  1903.     for (s = tmp+FROMLEN; *s == ' '; --s) *s = '\0';
  1904.     *++s = ':'; *++s = ' ';
  1905.     strcpy (++s,(mail_fetchstructure (stream,current,NIL))->subject);
  1906.     msg->subject = cpystr (tmp);/* set up subject */
  1907.                 /* get header of message */
  1908.     hdr = cpystr (mail_fetchheader (stream,current));
  1909.                 /* get body of message */
  1910.     text = mail_fetchtext (stream,current);
  1911.     if (body = send_text ()) {    /* get initial text of comments */
  1912.                 /* resize the forward text */
  1913.       fs_resize ((void **) &body->contents.text,
  1914.          (i = strlen ((char *) body->contents.text)) +
  1915.          strlen (fwdhdr) + strlen (hdr) + strlen (text));
  1916.       sprintf ((char *) body->contents.text + i,"%s%s%s",fwdhdr,hdr,text);
  1917.       send_level (msg,body);    /* enter send-level */
  1918.       mail_free_body (&body);
  1919.     }
  1920.     fs_give ((void **) &hdr);    /* free header */
  1921.     mail_free_envelope (&msg);    /* flush the message */
  1922.   }
  1923. }
  1924.  
  1925. /* HEADER command
  1926.  * Accepts: help flag
  1927.  */
  1928.  
  1929. void r_headers (short help)
  1930. {
  1931.   if (help) cmxprintf ("\
  1932. The HEADERS command displays one-line summaries of this message.\n");
  1933.   else {
  1934.     noise ("OF CURRENT MESSAGE");
  1935.     confirm ();
  1936.     header_message (stdout,current);
  1937.   }
  1938. }
  1939.  
  1940.  
  1941. /* HELP command
  1942.  * Accepts: help flag
  1943.  */
  1944.  
  1945. void r_help (short help)
  1946. {
  1947.   static fdb cmdfdb = {_CMKEY,NIL,NIL,(pdat) &(reatab),"Command, ",NIL,NIL};
  1948.   static fdb hlpfdb = {_CMCFM,NIL,&cmdfdb,NIL,NIL,NIL,NIL};
  1949.   pval parseval;
  1950.   fdb *used;
  1951.   if (help) cmxprintf ("\
  1952. The HELP command gives short descriptions of the MS read level commands.\n");
  1953.   else {
  1954.     noise ("WITH");
  1955.                 /* parse a command */
  1956.     parse (&hlpfdb,&parseval,&used);
  1957.     if (used == &hlpfdb) cmxprintf ("\
  1958. MS is at read level, in which commands apply only to the current message.\n");
  1959.     else {
  1960.       void *which = (void *) parseval._pvint;
  1961.       confirm ();
  1962.       do_help (which);        /* dispatch to appropriate command */
  1963.     }
  1964.   }
  1965. }
  1966.  
  1967. /* KEYWORD command
  1968.  * Accepts: help flag
  1969.  */
  1970.  
  1971. void r_keyword (short help)
  1972. {
  1973.   if (help) cmxprintf ("\
  1974. The KEYWORD command makes this message have the specified keyword.\n");
  1975.   else {
  1976.     char key[TMPLEN];
  1977.     char tmp[TMPLEN];
  1978.     pval parseval;
  1979.     fdb *used;
  1980.     static fdb flgfdb = {_CMKEY,NIL,NIL,(pdat) &(flgtab),"Keyword, ",NIL,NIL};
  1981.     noise ("NAME");
  1982.                 /* parse the keyword */
  1983.     parse (&flgfdb,&parseval,&used);
  1984.     strcpy (key,(char *) parseval._pvint);
  1985.     confirm ();
  1986.     sprintf (tmp,"%d",current);
  1987.     mail_setflag (stream,tmp,key);
  1988.   }
  1989. }
  1990.  
  1991.  
  1992. /* KILL command
  1993.  * Accepts: help flag
  1994.  */
  1995.  
  1996. void r_kill (short help)
  1997. {
  1998.   if (help) cmxprintf ("\
  1999. The KILL command deletes the current message and does an implicit NEXT.\n");
  2000.   else {
  2001.     char tmp[TMPLEN];
  2002.     noise ("MESSAGE");
  2003.     confirm ();
  2004.     sprintf (tmp,"%d",current);    /* delete the current message */
  2005.     mail_setflag (stream,tmp,"\\Deleted");
  2006.     done = -1;            /* exit this message */
  2007.   }
  2008. }
  2009.  
  2010. /* LITERAL-TYPE command
  2011.  * Accepts: help flag
  2012.  */
  2013.  
  2014. void r_literal_type (short help)
  2015. {
  2016.   if (help) cmxprintf ("\
  2017. The LITERAL-TYPE command types this message in original form.\n");
  2018.   else {
  2019.     noise ("MESSAGE");
  2020.     confirm ();
  2021.     if (stream) {
  2022.                 /* type the message */
  2023.       if (current) more (literal_type_message,current);
  2024.       else cmxprintf ("%%No current message\n");
  2025.     }
  2026.     else cmxprintf ("%%No mailbox is currently open\n");
  2027.   }
  2028. }
  2029.  
  2030.  
  2031. /* MARK command
  2032.  * Accepts: help flag
  2033.  */
  2034.  
  2035. void r_mark (short help)
  2036. {
  2037.   if (help) cmxprintf ("\
  2038. The MARK command makes this message be marked as seen.\n");
  2039.   else {
  2040.     char tmp[TMPLEN];
  2041.     noise ("MESSAGE");
  2042.     confirm ();
  2043.     sprintf (tmp,"%d",current);
  2044.     mail_setflag (stream,tmp,"\\Seen");
  2045.   }
  2046. }
  2047.  
  2048. /* MOVE command
  2049.  * Accepts: help flag
  2050.  */
  2051.  
  2052. void r_move (short help)
  2053. {
  2054.   if (help) cmxprintf ("\
  2055. The MOVE command moves this message into the specified mailbox\n\
  2056. and then deletes them from this mailbox.\n");
  2057.   else {
  2058.     char tmp[TMPLEN];
  2059.     char copybox[TMPLEN];
  2060.     pval parseval;
  2061.     fdb *used;
  2062.     static brktab mbxbrk = {
  2063.       {                /* 1st char break array */
  2064.     0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  2065.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1b
  2066.       },
  2067.       {                /* subsequent char break array */
  2068.                 /* same as above, plus dots */
  2069.     0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  2070.     0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1f
  2071.       }
  2072.     };
  2073.     static fdb mbxfdb = {_CMFLD,CM_SDH,NIL,NIL,"mailbox on this server",
  2074.                "INBOX",&mbxbrk};
  2075.     noise ("TO MAILBOX");
  2076.                 /* parse the mailbox name */
  2077.     parse (&mbxfdb,&parseval,&used);
  2078.     strcpy (copybox,atmbuf);    /* note the mailbox name */
  2079.     confirm ();
  2080.     sprintf (tmp,"%d",current);
  2081.     mail_move (stream,tmp,copybox);
  2082.   }
  2083. }
  2084.  
  2085. /* NEXT command
  2086.  * Accepts: help flag
  2087.  */
  2088.  
  2089. void r_next (short help)
  2090. {
  2091.   if (help) cmxprintf ("\
  2092. The NEXT command goes to the next message in the sequence.\n");
  2093.   else {
  2094.     noise ("MESSAGE");
  2095.     confirm ();
  2096.     done = -1;            /* let read level know it's time to next */
  2097.   }
  2098. }
  2099.  
  2100.  
  2101. /* PREVIOUS command
  2102.  * Accepts: help flag
  2103.  */
  2104.  
  2105. void r_previous (short help)
  2106. {
  2107.   if (help) cmxprintf ("\
  2108. The PREVIOUS command types the previous message in the mailbox.\n");
  2109.   else {
  2110.     int i;
  2111.     noise ("MESSAGE");
  2112.     confirm ();
  2113.                 /* look for earlier current message */
  2114.     for (i = current-1; i >= 1; --i) if (mail_elt (stream,i)->spare) {
  2115.       current = i;        /* this is the new current message */
  2116.       if (mail_elt (stream,current)->deleted)
  2117.     cmxprintf (" Message %d deleted\n",current);
  2118.       else {
  2119.     cmcls ();        /* zap the screen */
  2120.     more (type_message,current);
  2121.       }
  2122.       return;            /* skip error message */
  2123.     }
  2124.     cmxprintf (" Currently at beginning, message %d\n",current);
  2125.   }
  2126. }
  2127.  
  2128. /* QUIT command
  2129.  * Accepts: help flag
  2130.  */
  2131.  
  2132. void r_quit (short help)
  2133. {
  2134.   if (help) cmxprintf ("\
  2135. The QUIT command exits read level, returning to top level.\n");
  2136.   else {
  2137.     noise ("READ LEVEL");
  2138.     confirm ();
  2139.     done = T;            /* let read level know it's time to die */
  2140.   }
  2141. }
  2142.  
  2143.  
  2144. /* REMAIL command
  2145.  * Accepts: help flag
  2146.  */
  2147.  
  2148. void r_remail (short help)
  2149. {
  2150.   if (help) cmxprintf ("Remail this message to another mailbox.\n");
  2151.   else {
  2152.     pval parseval;
  2153.     fdb *used;
  2154.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,NIL,NIL,NIL};
  2155.     char *text;
  2156.     ENVELOPE *msg;
  2157.     ADDRESS *adr = NIL;
  2158.     noise ("MESSAGE TO");
  2159.     linfdb._cmhlp = "list of remail recipients in RFC 822 format";
  2160.     parse (&linfdb,&parseval,&used);
  2161.                 /* parse recipient */
  2162.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  2163.     if (!adr) {
  2164.       cmerr ("No remail recipient specified");
  2165.       return;
  2166.     }
  2167.     confirm ();
  2168.     remail_message (current,adr);
  2169.     mail_free_address (&adr);    /* flush the address */
  2170.   }
  2171. }
  2172.  
  2173. /* STATUS command
  2174.  * Accepts: help flag
  2175.  */
  2176.  
  2177. void r_status (short help)
  2178. {
  2179.   if (help) cmxprintf ("Type status of current mailbox.\n");
  2180.   else {
  2181.     noise ("OF CURRENT MAILBOX");
  2182.     confirm ();
  2183.     if (stream) {
  2184.       do_status (stream);    /* output the status */
  2185.       if (current) cmxprintf (" Currently at message %d\n",current);
  2186.     }
  2187.     else cmxprintf ("%%No mailbox is currently open\n");
  2188.   }
  2189. }
  2190.  
  2191.  
  2192. /* TYPE command
  2193.  * Accepts: help flag
  2194.  */
  2195.  
  2196. void r_type (short help)
  2197. {
  2198.   if (help) cmxprintf ("The TYPE command types this message.\n");
  2199.   else {
  2200.     noise ("MESSAGE");
  2201.     confirm ();
  2202.     if (stream) {
  2203.                 /* type the message */
  2204.       if (current) more (type_message,current);
  2205.       else cmxprintf ("%%No current message\n");
  2206.     }
  2207.     else cmxprintf ("%%No mailbox is currently open\n");
  2208.   }
  2209. }
  2210.  
  2211. /* UNANSWER command
  2212.  * Accepts: help flag
  2213.  */
  2214.  
  2215. void r_unanswer (short help)
  2216. {
  2217.   if (help) cmxprintf ("\
  2218. The UNANSWER command makes this message not be answered.\n");
  2219.   else {
  2220.     char tmp[TMPLEN];
  2221.     noise ("MESSAGE");
  2222.     confirm ();
  2223.     sprintf (tmp,"%d",current);
  2224.     mail_clearflag (stream,tmp,"\\Answered");
  2225.   }
  2226. }
  2227.  
  2228.  
  2229. /* UNDELETE command
  2230.  * Accepts: help flag
  2231.  */
  2232.  
  2233. void r_undelete (short help)
  2234. {
  2235.   if (help) cmxprintf ("\
  2236. The UNDELETE command makes this message not be deleted.\n");
  2237.   else {
  2238.     char tmp[TMPLEN];
  2239.     noise ("MESSAGE");
  2240.     confirm ();
  2241.     sprintf (tmp,"%d",current);
  2242.     mail_clearflag (stream,tmp,"\\Deleted");
  2243.   }
  2244. }
  2245.  
  2246.  
  2247. /* UNFLAG command
  2248.  * Accepts: help flag
  2249.  */
  2250.  
  2251. void r_unflag (short help)
  2252. {
  2253.   if (help) cmxprintf ("\
  2254. The UNFLAG command makes this message not be flagged as urgent.\n");
  2255.   else {
  2256.     char tmp[TMPLEN];
  2257.     noise ("MESSAGE");
  2258.     confirm ();
  2259.     sprintf (tmp,"%d",current);
  2260.     mail_clearflag (stream,tmp,"\\Flagged");
  2261.   }
  2262. }
  2263.  
  2264. /* UNKEYWORD command
  2265.  * Accepts: help flag
  2266.  */
  2267.  
  2268. void r_unkeyword (short help)
  2269. {
  2270.   if (help) cmxprintf ("\
  2271. The UNKEYWORD command makes this message not have the specified keyword.\n");
  2272.   else {
  2273.     char key[TMPLEN];
  2274.     char tmp[TMPLEN];
  2275.     pval parseval;
  2276.     fdb *used;
  2277.     static fdb flgfdb = {_CMKEY,NIL,NIL,(pdat) &(flgtab),"Keyword, ",NIL,NIL};
  2278.     noise ("NAME");
  2279.                 /* parse the keyword */
  2280.     parse (&flgfdb,&parseval,&used);
  2281.     strcpy (key,(char *) parseval._pvint);
  2282.     confirm ();
  2283.     sprintf (tmp,"%d",current);
  2284.     mail_clearflag (stream,tmp,key);
  2285.   }
  2286. }
  2287.  
  2288.  
  2289. /* UNMARK command
  2290.  * Accepts: help flag
  2291.  */
  2292.  
  2293. void r_unmark (short help)
  2294. {
  2295.   if (help) cmxprintf ("\
  2296. The UNMARK command makes this message not be marked as seen.\n");
  2297.   else {
  2298.     char tmp[TMPLEN];
  2299.     noise ("MESSAGE");
  2300.     confirm ();
  2301.     sprintf (tmp,"%d",current);
  2302.     mail_clearflag (stream,tmp,"\\Seen");
  2303.   }
  2304. }
  2305.  
  2306. /* Send command level */
  2307.  
  2308.  
  2309. /* Send level command table */
  2310.  
  2311. static keywrd sendcmds[] = {
  2312.   {"BBOARDS",    NIL,    (keyval) s_bboards},
  2313.   {"BCC",    NIL,    (keyval) s_bcc},
  2314.   {"BLANK",    NIL,    (keyval) c_blank},
  2315.   {"CC",    NIL,    (keyval) s_cc},
  2316.   {"D",        KEY_INV|KEY_ABR,(keyval) 7},
  2317.   {"DAYTIME",    NIL,    (keyval) c_daytime},
  2318.   {"DEBUG",    NIL,    (keyval) c_debug},
  2319.   {"DISPLAY",    NIL,    (keyval) s_display},
  2320.   {"ECHO",    NIL,    (keyval) c_echo},
  2321.   {"ERASE",    NIL,    (keyval) s_erase},
  2322.   {"HELP",    NIL,    (keyval) s_help},
  2323.   {"LITERAL-TYPE",NIL,    (keyval) r_literal_type},
  2324.   {"QUIT",    NIL,    (keyval) s_quit},
  2325.   {"REMOVE",    NIL,    (keyval) s_remove},
  2326.   {"SEND",    NIL,    (keyval) s_send},
  2327.   {"STATUS",    NIL,    (keyval) r_status},
  2328.   {"SUBJECT",    NIL,    (keyval) s_subject},
  2329.   {"TO",    NIL,    (keyval) s_to},
  2330.   {"TYPE",    NIL,    (keyval) r_type},
  2331. };
  2332. static keytab sndtab = {(sizeof (sendcmds)/sizeof (keywrd)),sendcmds};
  2333.  
  2334.  
  2335. /* Send command level
  2336.  * Accepts: message
  2337.  */
  2338.  
  2339. void send_level (ENVELOPE *msg,BODY *body)
  2340. {
  2341.   pval parseval;
  2342.   fdb *used;
  2343.   static fdb sndfdb = {_CMKEY,NIL,NIL,(pdat) &(sndtab),"Command, ","SEND",NIL};
  2344.   int olddone = done;        /* hold calling done */
  2345.   done = NIL;            /* not done in send yet */
  2346.   while (!done) {        /* loop until done */
  2347.     cmseter ();            /* set error trap */
  2348.                 /* exit on EOF */
  2349.     if (cmcsb._cmerr == CMxEOF) break;
  2350.     prompt ("MS-Send>");    /* prompt */
  2351.     cmsetrp ();            /* set reparse trap */
  2352.                 /* parse command */
  2353.     parse (&sndfdb,&parseval,&used);
  2354.                 /* do the command */
  2355.     do_scmd ((void *) parseval._pvint,msg,body);
  2356.   }
  2357.   done = olddone;        /* so we don't bust out of top level */
  2358. }
  2359.  
  2360. /* Execute command
  2361.  * Accepts: function
  2362.  *        message
  2363.  */
  2364.  
  2365. void do_scmd (int (*f)(short help,ENVELOPE *msg,BODY *body),
  2366.           ENVELOPE *msg,BODY *body)
  2367. {
  2368.   (*f)((short) NIL,msg,body);    /* call function with help flag off */
  2369. }
  2370.  
  2371.  
  2372. /* Execute help for command
  2373.  * Accepts: function
  2374.  *        message
  2375.  */
  2376.  
  2377. void do_shelp (int (*f)(short help,ENVELOPE *msg,BODY *body),
  2378.            ENVELOPE *msg,BODY *body)
  2379. {
  2380.   (*f)((short) T,msg,body);    /* call function with help flag on */
  2381. }
  2382.  
  2383. /* Send command execution routines */
  2384.  
  2385.  
  2386. /* BBOARDS command
  2387.  * Accepts: help flag
  2388.  *        message
  2389.  */
  2390.  
  2391. void s_bboards (short help,ENVELOPE *msg,BODY *body)
  2392. {
  2393.   if (help) cmxprintf ("\
  2394. The BBOARDS command sets a new bulletin boards list.\n");
  2395.   else {
  2396.     char newsgroups[TMPLEN];
  2397.     pval parseval;
  2398.     fdb *used;
  2399.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"list of BBoards",NIL,NIL};
  2400.                 /* get newsgroups */
  2401.     parse (&linfdb,&parseval,&used);
  2402.     strcpy (newsgroups,atmbuf);    /* copy newsgroups into temp buffer */
  2403.     confirm ();
  2404.                 /* flush the old newsgroups */
  2405.     if (msg->newsgroups) fs_give ((void **) &msg->newsgroups);
  2406.                 /* set new newsgroups */
  2407.     msg->newsgroups = newsgroups[0] ? cpystr (newsgroups) : NIL;
  2408.   }
  2409. }
  2410.  
  2411. /* BCC command
  2412.  * Accepts: help flag
  2413.  *        message
  2414.  */
  2415.  
  2416. void s_bcc (short help,ENVELOPE *msg,BODY *body)
  2417. {
  2418.   if (help) cmxprintf ("\
  2419. The BCC command adds recipients to the blind carbon copy (bcc) list.\n");
  2420.   else {
  2421.     ADDRESS *adr = NIL;
  2422.     ADDRESS *lst;
  2423.     pval parseval;
  2424.     fdb *used;
  2425.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"blind carbon copy list",
  2426.                NIL,NIL};
  2427.     noise ("TO");
  2428.     parse (&linfdb,&parseval,&used);
  2429.                 /* free old one in case reparse */
  2430.     if (adr) mail_free_address (&adr);
  2431.                 /* parse the address list */
  2432.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  2433.     confirm ();
  2434.     if (lst = msg->bcc) {    /* if a bcc list already */
  2435.                 /* run down the list until the end */
  2436.       while (lst->next) lst = lst->next;
  2437.       lst->next = adr;        /* and link at the end of the list */
  2438.     }
  2439.     else msg->bcc = adr;    /* else this is the bcc list */
  2440.   }
  2441. }
  2442.  
  2443. /* CC command
  2444.  * Accepts: help flag
  2445.  *        message
  2446.  */
  2447.  
  2448. void s_cc (short help,ENVELOPE *msg,BODY *body)
  2449. {
  2450.   if (help) cmxprintf ("\
  2451. The CC command adds recipients to the carbon copy (cc) list.\n");
  2452.   else {
  2453.     ADDRESS *adr = NIL;
  2454.     ADDRESS *lst;
  2455.     pval parseval;
  2456.     fdb *used;
  2457.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"carbon copy list",
  2458.                NIL,NIL};
  2459.     noise ("TO");
  2460.     parse (&linfdb,&parseval,&used);
  2461.                 /* free old one in case reparse */
  2462.     if (adr) mail_free_address (&adr);
  2463.                 /* parse the address list */
  2464.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  2465.     confirm ();
  2466.     if (lst = msg->cc) {    /* if a cc list already */
  2467.                 /* run down the list until the end */
  2468.       while (lst->next) lst = lst->next;
  2469.       lst->next = adr;        /* and link at the end of the list */
  2470.     }
  2471.     else msg->cc = adr;        /* else this is the cc list */
  2472.   }
  2473. }
  2474.  
  2475.  
  2476. /* DISPLAY command
  2477.  * Accepts: help flag
  2478.  *        message
  2479.  */
  2480.  
  2481. void s_display (short help,ENVELOPE *msg,BODY *body)
  2482. {
  2483.   void *message[2];
  2484.   if (help) cmxprintf ("\
  2485. The DISPLAY command displays the header and text of the message.\n");
  2486.   else {
  2487.     noise ("MESSAGE");
  2488.     confirm ();
  2489.     message[0] = (void *) msg;
  2490.     message[1] = (void *) body;
  2491.     more (do_display,(long) message);
  2492.   }
  2493. }
  2494.  
  2495. /* ERASE command
  2496.  * Accepts: help flag
  2497.  *        message
  2498.  */
  2499.  
  2500. #define ERBCC 0
  2501. #define ERCC 1
  2502. #define ERRNEWS 2
  2503. #define ERTO 3
  2504.  
  2505. static keywrd eracmds[] = {
  2506.   {"BBOARDS",    NIL,    (keyval) ERRNEWS},
  2507.   {"BCC",    NIL,    (keyval) ERBCC},
  2508.   {"CC",    NIL,    (keyval) ERCC},
  2509.   {"TO",    NIL,    (keyval) ERTO},
  2510. };
  2511. static keytab eratab = {(sizeof (eracmds)/sizeof (keywrd)),eracmds};
  2512.  
  2513.  
  2514. void s_erase (short help,ENVELOPE *msg,BODY *body)
  2515. {
  2516.   if (help) cmxprintf ("\
  2517. The ERASE command erases the specified recipient list.\n");
  2518.   else {
  2519.     int i;
  2520.     pval parseval;
  2521.     fdb *used;
  2522.     static fdb erafdb = {_CMKEY,NIL,NIL,(pdat) &(eratab),"Address list, ",
  2523.                NIL,NIL};
  2524.     noise ("LIST");
  2525.     parse (&erafdb,&parseval,&used);
  2526.     i = parseval._pvint;    /* save user's selection */
  2527.     confirm ();
  2528.     switch (i) {        /* now zap the appropriate list */
  2529.     case ERBCC:            /* bcc list */
  2530.       if (msg->bcc) mail_free_address (&msg->bcc);
  2531.       break;
  2532.     case ERCC:            /* cc list */
  2533.       if (msg->cc) mail_free_address (&msg->cc);
  2534.       break;
  2535.     case ERRNEWS:
  2536.       if (msg->newsgroups) fs_give ((void **) &msg->newsgroups);
  2537.       break;
  2538.     case ERTO:            /* to list */
  2539.     default:
  2540.       if (msg->to) mail_free_address (&msg->to);
  2541.       break;
  2542.     }
  2543.   }
  2544. }
  2545.  
  2546. /* HELP command
  2547.  * Accepts: help flag
  2548.  *        message
  2549.  */
  2550.  
  2551. void s_help (short help,ENVELOPE *msg,BODY *body)
  2552. {
  2553.   static fdb sndfdb = {_CMKEY,NIL,NIL,(pdat) &(sndtab),"Command, ",NIL,NIL};
  2554.   static fdb hlpfdb = {_CMCFM,NIL,&sndfdb,NIL,NIL,NIL,NIL};
  2555.   pval parseval;
  2556.   fdb *used;
  2557.   if (help) cmxprintf ("\
  2558. The HELP command gives short descriptions of the send-level MS commands.\n");
  2559.   else {
  2560.     noise ("WITH");
  2561.                 /* parse a command */
  2562.     parse (&hlpfdb,&parseval,&used);
  2563.     if (used == &hlpfdb) cmxprintf ("\
  2564. You are at MS send level, which allows you to change various parts of your\n\
  2565. message prior to sending it.\n");
  2566.     else {
  2567.       void *which = (void *) parseval._pvint;
  2568.       confirm ();
  2569.       do_shelp (which,msg,body);/* dispatch to appropriate command */
  2570.     }
  2571.   }
  2572. }
  2573.  
  2574.  
  2575. /* QUIT command
  2576.  * Accepts: help flag
  2577.  *        message
  2578.  */
  2579.  
  2580. void s_quit (short help,ENVELOPE *msg,BODY *body)
  2581. {
  2582.   if (help) cmxprintf ("\
  2583. The QUIT command aborts the message being composed and returns to top level.\
  2584. \n");
  2585.   else {
  2586.     noise ("SEND LEVEL");
  2587.     confirm ();
  2588.     done = T;            /* let send level know it's time to die */
  2589.     quitted = T;        /* flag to zap answered */
  2590.   }
  2591. }
  2592.  
  2593. /* REMOVE command
  2594.  * Accepts: help flag
  2595.  *        message
  2596.  */
  2597.  
  2598. void s_remove (short help,ENVELOPE *msg,BODY *body)
  2599. {
  2600.   if (help) cmxprintf ("\
  2601. The REMOVE command removes the specified recipient.\n");
  2602.   else {
  2603.     char text[TMPLEN];
  2604.     pval parseval;
  2605.     fdb *used;
  2606.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"recipient mailbox",NIL,NIL};
  2607.     noise ("RECIPIENT");
  2608.                 /* get subject */
  2609.     parse (&linfdb,&parseval,&used);
  2610.     strcpy (text,atmbuf);    /* copy text into temp buffer */
  2611.     confirm ();
  2612.                 /* remove the recipient from all lists */
  2613.     msg->to = remove_adr (msg->to,text);
  2614.     msg->cc = remove_adr (msg->cc,text);
  2615.     msg->bcc = remove_adr (msg->bcc,text);
  2616.   }
  2617. }
  2618.  
  2619. /* SEND command
  2620.  * Accepts: help flag
  2621.  *        message
  2622.  */
  2623.  
  2624. void s_send (short help,ENVELOPE *msg,BODY *body)
  2625. {
  2626.   if (help) cmxprintf ("Send this message.\n");
  2627.   else {
  2628. #if unix
  2629.     int i = 0;
  2630.     unsigned char *text = body->contents.text;
  2631.     unsigned char *src = body->contents.text;
  2632.     unsigned char *dst;
  2633. #endif
  2634.     noise ("MESSAGE");
  2635.     confirm ();
  2636. #if unix
  2637.                 /* count LF's in string */
  2638.     while (*src) if (*src++ == '\012') i++;
  2639.     body->contents.text =     /* get space for destination string */
  2640.       (dst = (unsigned char *) fs_get (1+i + (src - body->contents.text)));
  2641.     src = text;            /* source string */
  2642.     while (*src) {        /* copy string, inserting CR's before LF's */
  2643.                 /* if CR, copy it and skip LF check for next */
  2644.       if (*src == '\015') *dst++ = *src++;
  2645.                 /* else if LF, insert a CR before it */
  2646.       else if (*src == '\012') *dst++ = '\015';
  2647.       if (*src) *dst++ = *src++;/* copy (may be null if last char was CR) */
  2648.     }
  2649.     *dst = '\0';        /* tie off destination */
  2650.     send_message (msg,body);    /* send the message */
  2651.     body->contents.text = text;    /* restore original */
  2652. #else
  2653.     send_message (msg,body);    /* send the message */
  2654. #endif
  2655.     quitted = NIL;        /* flag to zap answered */
  2656.   }
  2657. }
  2658.  
  2659. /* SUBJECT command
  2660.  * Accepts: help flag
  2661.  *        message
  2662.  */
  2663.  
  2664. void s_subject (short help,ENVELOPE *msg,BODY *body)
  2665. {
  2666.   if (help) cmxprintf ("\
  2667. The SUBJECT command sets the subject of this message.\n");
  2668.   else {
  2669.     char subject[TMPLEN];
  2670.     pval parseval;
  2671.     fdb *used;
  2672.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"subject text",NIL,NIL};
  2673.                 /* get subject */
  2674.     parse (&linfdb,&parseval,&used);
  2675.     strcpy (subject,atmbuf);    /* copy subject into temp buffer */
  2676.     confirm ();
  2677.                 /* flush the old subject */
  2678.     if (msg->subject) fs_give ((void **) &msg->subject);
  2679.                 /* set new subject */
  2680.     msg->subject = subject[0] ? cpystr (subject) : NIL;
  2681.   }
  2682. }
  2683.  
  2684. /* TO command
  2685.  * Accepts: help flag
  2686.  *        message
  2687.  */
  2688.  
  2689. void s_to (short help,ENVELOPE *msg,BODY *body)
  2690. {
  2691.   if (help) cmxprintf ("\
  2692. The TO command adds recipients to the primary recipient (to) list.\n");
  2693.   else {
  2694.     ADDRESS *adr = NIL;
  2695.     ADDRESS *lst;
  2696.     pval parseval;
  2697.     fdb *used;
  2698.     static fdb linfdb = {_CMTXT,CM_SDH,NIL,NIL,"to list",
  2699.                NIL,NIL};
  2700.     noise ("TO");
  2701.     parse (&linfdb,&parseval,&used);
  2702.                 /* free old one in case reparse */
  2703.     if (adr) mail_free_address (&adr);
  2704.                 /* parse the address list */
  2705.     rfc822_parse_adrlist (&adr,atmbuf,curhst);
  2706.     confirm ();
  2707.     if (lst = msg->to) {    /* if a to list already */
  2708.                 /* run down the list until the end */
  2709.       while (lst->next) lst = lst->next;
  2710.       lst->next = adr;        /* and link at the end of the list */
  2711.     }
  2712.     else msg->to = adr;        /* else this is the to list */
  2713.   }
  2714. }
  2715.  
  2716. /* Sequence parser  */
  2717.  
  2718.  
  2719. #define s_date 1
  2720. #define s_flag 2
  2721. #define s_string 3
  2722.  
  2723. static keywrd seqcmds[] = {
  2724.   {"ALL",    NIL,    (keyval) NIL},
  2725.   {"ANSWERED",    NIL,    (keyval) NIL},
  2726.   {"BCC",    NIL,    (keyval) s_string},
  2727.   {"BEFORE",    NIL,    (keyval) s_date},
  2728.   {"BODY",    NIL,    (keyval) s_string},
  2729.   {"CC",    NIL,    (keyval) s_string},
  2730.   {"DELETED",    NIL,    (keyval) NIL},
  2731.   {"FLAGGED",    NIL,    (keyval) NIL},
  2732.   {"FROM",    NIL,    (keyval) s_string},
  2733.   {"KEYWORD",    NIL,    (keyval) s_flag},
  2734.   {"NEW",    NIL,    (keyval) NIL},
  2735.   {"OLD",    NIL,    (keyval) NIL},
  2736.   {"ON",    NIL,    (keyval) s_date},
  2737.   {"RECENT",    NIL,    (keyval) NIL},
  2738.   {"SEEN",    NIL,    (keyval) NIL},
  2739.   {"SINCE",    NIL,    (keyval) s_date},
  2740.   {"SUBJECT",    NIL,    (keyval) s_string},
  2741.   {"TEXT",    NIL,    (keyval) s_string},
  2742.   {"TO",    NIL,    (keyval) s_string},
  2743.   {"UNANSWERED",NIL,    (keyval) NIL},
  2744.   {"UNDELETED",    NIL,    (keyval) NIL},
  2745.   {"UNFLAGGED",    NIL,    (keyval) NIL},
  2746.   {"UNKEYWORD",    NIL,    (keyval) s_flag},
  2747.   {"UNSEEN",    NIL,    (keyval) NIL},
  2748. };
  2749. static keytab seqtab = {(sizeof (seqcmds)/sizeof (keywrd)),seqcmds};
  2750.  
  2751. static keywrd seq2cmds[] = {
  2752.   {"LAST",    NIL,    (keyval) T},
  2753. };
  2754. static keytab seq2tab = {(sizeof (seq2cmds)/sizeof (keywrd)),seq2cmds};
  2755.  
  2756. /* Sequence parser
  2757.  * Accepts: default string
  2758.  * Returns: first element of the sequence as a success flag
  2759.  */
  2760.  
  2761. char do_sequence (char *def)
  2762. {
  2763.   int i,j;
  2764.   int msgno;
  2765.   char tmp[TMPLEN];
  2766.   char tmpx[TMPLEN];
  2767.   char *seq;
  2768.   pval parseval;
  2769.   fdb *used;
  2770.   static fdb cmdfdb = {_CMKEY,NIL,NIL,(pdat) &(seqtab),"Message sequence, ",
  2771.              NIL,NIL};
  2772.   static fdb cm2fdb = {_CMKEY,NIL,NIL,(pdat) &(seq2tab),NIL,NIL,NIL};
  2773.   static fdb flgfdb = {_CMKEY,NIL,NIL,(pdat) &(flgtab),"Keyword, ",NIL,NIL};
  2774.   static fdb numfdb = {_CMNUM,NIL,NIL,(pdat) 10,NIL,NIL,NIL};
  2775.   static fdb cfmfdb = {_CMCFM,NIL,NIL,NIL,NIL,NIL,NIL};
  2776.   static fdb datfdb = {_CMTAD,DTP_NTI,NIL,NIL,NIL,NIL};
  2777.   static fdb qstfdb = {_CMQST,NIL,NIL,NIL,NIL,NIL,NIL};
  2778.   static fdb fldfdb = {_CMFLD,NIL,NIL,NIL,NIL,NIL,NIL};
  2779.   static fdb cmafdb = {_CMTOK,CM_SDH,NIL,",","comma for another number",NIL,
  2780.              NIL};
  2781.   static fdb clnfdb = {_CMTOK,CM_SDH,NIL,":","colon for a range",NIL,NIL};
  2782.   if (!stream) {
  2783.     cmerr ("No mailbox is currently open");
  2784.     return (NIL);
  2785.   }
  2786.   qstfdb._cmlst = (fdb *) &fldfdb;
  2787.   cmdfdb._cmlst = (fdb *) &cm2fdb;
  2788.   cm2fdb._cmlst = (fdb *) &numfdb;
  2789.   cmdfdb._cmdef = def;        /* default is first whatever was in call */
  2790.                 /* else default is previous sequence if any */
  2791.   numfdb._cmdef = sequence[0] ? sequence : NIL;
  2792.   clnfdb._cmlst = (fdb *) &cmafdb;
  2793.   cmafdb._cmlst = (fdb *) &cfmfdb;
  2794.   noise ("MESSAGES");
  2795.                 /* get first sequence item */
  2796.   parse (&cmdfdb,&parseval,&used);
  2797.   if (used == &numfdb) {
  2798.                 /* invalidate the current sequence */
  2799.     for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  2800.     while (used != &cfmfdb) {
  2801.       i = parseval._pvint;    /* note message number */
  2802.       if (i < 1 || i > nmsgs) {
  2803.     cmerr ("Invalid message number");
  2804.     return (NIL);
  2805.       }
  2806.                 /* get what's after number */
  2807.       parse (&clnfdb,&parseval,&used);
  2808.                 /* select just this message if not range */
  2809.       if (used != &clnfdb) mail_elt (stream,i)->spare = T;
  2810.       else {            /* user wants a range */
  2811.     parse (&numfdb,&parseval,&used);
  2812.     if (parseval._pvint < 1 || parseval._pvint > nmsgs) {
  2813.       cmerr ("Invalid message number");
  2814.       return (NIL);
  2815.     }
  2816.                 /* reverse range? */
  2817.     if (i > parseval._pvint) {
  2818.       j = i;        /* yes, reverse it back again */
  2819.       i = parseval._pvint;
  2820.     }
  2821.     else j = parseval._pvint;
  2822.                 /* set range to T */
  2823.     do mail_elt (stream,i++)->spare = T;
  2824.     while (i <= j);
  2825.                 /* get what's after range */
  2826.     parse (&cmafdb,&parseval,&used);
  2827.       }
  2828.                 /* if got a comma, get another number */
  2829.       if (used == &cmafdb) parse (&numfdb,&parseval,&used);
  2830.     }
  2831.   }
  2832.   else {
  2833.     tmp[0] = '\0';        /* init search buffer */
  2834.                 /* init selection */
  2835.     for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = T;
  2836.                 /* numbers not allowed any more */
  2837.     cm2fdb._cmlst = (fdb *) &cfmfdb;
  2838.     do {
  2839.       if (used == &cmdfdb) {    /* command for mail_search? */
  2840.                 /* prepend a space if not the first time */
  2841.     if (tmp[0] != '\0') strcat (tmp," ");
  2842.     /* This is a disgusting kludge.  I'm ashamed to admit having written
  2843.        it, but not as much as the authors of CCMD should be for having
  2844.        cmkey parsing return the data item instead of the entire keyword
  2845.        block. */
  2846.     ucase (atmbuf);        /* strncmp is case-dependent */
  2847.                 /* locate the keyword */
  2848.     for (i = 0; strncmp (seqcmds[i]._kwkwd,atmbuf,strlen (atmbuf)); i++);
  2849.                 /* see what we need to do */
  2850.     switch (parseval._pvint) {
  2851.     case s_date:        /* slurp date */
  2852.       parse (&datfdb,&parseval,&used);
  2853.       sprintf (tmpx,"%s %d/%d/%d",seqcmds[i]._kwkwd,
  2854.            (&parseval._pvtad)->_dtmon+1,(&parseval._pvtad)->_dtday+1,
  2855.            (&parseval._pvtad)->_dtyr);
  2856.       strcat (tmp,tmpx);    /* append string */
  2857.       break;
  2858.     case s_flag:        /* slurp keyword */
  2859.       parse (&flgfdb,&parseval,&used);
  2860.       sprintf (tmpx,"%s %s",seqcmds[i]._kwkwd,(char *) parseval._pvint);
  2861.       strcat (tmp,tmpx);    /* append string */
  2862.       break;
  2863.     case s_string:        /* slurp quoted string or field */
  2864.       parse (&qstfdb,&parseval,&used);
  2865.       sprintf (tmpx,"%s \"%s\"",seqcmds[i]._kwkwd,atmbuf);
  2866.       strcat (tmp,tmpx);    /* append string */
  2867.       break;
  2868.     default:        /* command that doesn't take an argument */
  2869.       strcat (tmp,seqcmds[i]._kwkwd);
  2870.       break;
  2871.     }
  2872.       }
  2873.       else {            /* local command, LAST only one so far */
  2874.     parse (&numfdb,&parseval,&used);
  2875.     if (parseval._pvint < 1 || parseval._pvint > nmsgs) {
  2876.       cmerr ("Invalid number of messages");
  2877.       return (NIL);
  2878.     }
  2879.                 /* reject specified number of messages */
  2880.     for (i = 1; i <= stream->nmsgs - parseval._pvint; i++)
  2881.       mail_elt (stream,i)->spare = NIL;
  2882.       }
  2883.       parse (&cmdfdb,&parseval,&used);
  2884.     } while (used != &cfmfdb);    /* loop until confirm */
  2885.     sequence[current = 0] = '\0';
  2886.     if (tmp[0]) {        /* search needed? */
  2887.       mail_search (stream,tmp);
  2888.       for (i = 1; i <= nmsgs; ++i)
  2889.     mail_elt (stream,i)->spare &= mail_elt (stream,i)->searched;
  2890.     }
  2891.   }
  2892.                 /* recompute sequence string */
  2893.   seq = sequence;        /* start sequence pointer */
  2894.   *seq = '\0';            /* destroy old sequence poop */
  2895.   i = NIL;            /* delimiter is null first time through */
  2896.   for (msgno = 1; msgno <= nmsgs; ++msgno)
  2897.     if (mail_elt (stream,msgno)->spare) {
  2898.                 /* if at the last message or next not sel */
  2899.                 /* output delimiter and the number */
  2900.       sprintf (seq,"%s%d",(i ? "," : ""),msgno);
  2901.       /* The following kludge is necessary because the damn VAX C library has
  2902.      sprintf return a char * rather than an int!  The comment by it in
  2903.      stdio.h is "too painful to do right".  The cretin responsible should
  2904.      be drawn and quartered. */
  2905.       seq += (i = strlen (seq));/* update the pointer */
  2906.       current = msgno;        /* this is the new current */
  2907.                 /* any consecutive entries? */
  2908.       if (msgno < nmsgs && mail_elt (stream,msgno+1)->spare) {
  2909.                 /* yes, look for end of range */
  2910.     while (msgno < nmsgs && mail_elt (stream,msgno+1)->spare) msgno++;
  2911.     sprintf (seq,":%d",msgno);/* output delimiter and final of range */
  2912.     seq += (i = strlen (seq));/* (see above kludge note) */
  2913.     current = msgno;    /* this is the new current */
  2914.       }
  2915.     }
  2916.   return (sequence[0]);        /* flag if any sequence found */
  2917. }
  2918.  
  2919. /* Command subroutines */
  2920.  
  2921.  
  2922. /* Execute code under "more"
  2923.  * Accepts: function to be called
  2924.  *        integer argument to function
  2925.  */
  2926.  
  2927. void more (void (*f)(FILE *pipe,long arg),long arg)
  2928. {
  2929. #if unix
  2930.   FILE *pipe = popen ("more","w");
  2931.   if (pipe) {
  2932.     fflush (stdout);        /* make sure regular output is done */
  2933.     fflush (stderr);
  2934.     critical = T;        /* ignore CTRL/C while more is running */
  2935.     (*f)(pipe,arg);
  2936.     fflush (pipe);        /* wait for output to be done */
  2937.     pclose (pipe);        /* close the pipe */
  2938.     critical = NIL;        /* allow CTRL/C again */
  2939.   }
  2940.   else (*f)(stdout,arg);    /* do it this way if can't invoke "more" */
  2941. #else
  2942.   (*f)(stdout,arg);        /* non-Unix system */
  2943. #endif
  2944. }
  2945.  
  2946.  
  2947. /* Display current message
  2948.  * Accepts: file to output to
  2949.  *        integer of message pointer
  2950.  */
  2951.  
  2952. void do_display (FILE *pipe,long i)
  2953. {
  2954.   ENVELOPE *msg = (ENVELOPE *) *(void **) i;
  2955.   BODY *body = (BODY *) *++(void **) i;
  2956.   char header[8196];
  2957.   rfc822_header (header,msg,body);
  2958.   fprintf (pipe,"%s%s\n",header,body->contents.text);
  2959. }
  2960.  
  2961.  
  2962. /* Do headers of selected messages
  2963.  * Accepts: file to output to
  2964.  *        dummy
  2965.  */
  2966.  
  2967. void do_header (FILE *pipe,long i)
  2968. {
  2969.   for (i = 1; i <= nmsgs; ++i) if (mail_elt (stream,i)->spare)
  2970.     header_message (pipe,i);
  2971. }
  2972.  
  2973. /* Get a mailbox
  2974.  * Accepts: mailbox name
  2975.  */
  2976.  
  2977. void do_get (char *mailbox)
  2978. {
  2979.   register int i;
  2980.   char *s,tmp[TMPLEN],lsthst[TMPLEN];
  2981.   nmsgs = 0;            /* init number of messages */
  2982.   flgtab._ktcnt = 0;        /* re-init keyword table */
  2983.   if (stream && (s = (*stream->mailbox == '{') ? stream->mailbox :
  2984.          (((*stream->mailbox == '*')&&(stream->mailbox[1] == '{')) ?
  2985.           stream->mailbox + 1 : NIL))) {
  2986.     strcpy (lsthst,s);        /* copy last host */
  2987.     if (s = strchr (lsthst,'}')) s[1] = '\0';
  2988.   }
  2989.   else lsthst[0] = '\0';    /* no last host */
  2990.                 /* open new connection */
  2991.   if (stream = mail_open (stream,mailbox,debug ? OP_DEBUG : NIL)) {
  2992.     for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->spare = NIL;
  2993.     sequence[current = 0] = '\0';
  2994.     nmsgs = stream->nmsgs;    /* in case mail_open "failed" */
  2995.     do_status (stream);        /* report status of mailbox */
  2996.                 /* copy keywords to our table */
  2997.     for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i) {
  2998.                 /* value and keyword are the same */
  2999.       flags[i]._kwval = (keyval) (flags[i]._kwkwd = stream->user_flags[i]);
  3000.       flags[i]._kwflg = NIL;    /* no special flags */
  3001.     }
  3002.     flgtab._ktcnt = i;        /* update keyword count */
  3003.     if (*stream->mailbox == '{' || ((*stream->mailbox == '*') &&
  3004.                     (stream->mailbox[1] == '{'))) {
  3005.       strcpy (tmp,strchr (stream->mailbox,'{'));
  3006.       if (s = strchr (tmp,'}')) s[1] = '\0';
  3007.       if (strcmp (tmp,lsthst)) {/* find remote bboards */
  3008.     sprintf (lsthst,"%s*",tmp);
  3009.     mail_find (stream,lsthst);
  3010.     mail_find_bboards (stream,lsthst);
  3011.       }
  3012.     }
  3013.   }
  3014. }
  3015.  
  3016.  
  3017. /* Report status of the current mailbox
  3018.  * Accepts: stream
  3019.  */
  3020.  
  3021. void do_status (MAILSTREAM *stream)
  3022. {
  3023.   char *s = stream->mailbox;
  3024.   char *m = "BBoard";
  3025.   if (s) {            /* report status */
  3026.     if (*s == '*') s++;        /* is it a bboard? */
  3027.     else m = stream->readonly ? "Read-only mailbox" : "Mailbox";
  3028.     cmxprintf (" %s: %s, %d messages, %d recent\n",m,s,nmsgs,stream->recent);
  3029.   }
  3030. }
  3031.  
  3032.  
  3033. /* Display version of this program */
  3034.  
  3035. void do_version ()
  3036. {
  3037. #if unix
  3038.   char tmp[TMPLEN];
  3039.   char *local;
  3040.   struct hostent *host_name;
  3041.   gethostname (tmp,TMPLEN);    /* get local name */
  3042.                 /* get it in full form */
  3043.   local = (host_name = gethostbyname (tmp)) ? host_name->h_name : tmp;
  3044.   cmxprintf (" %s",local);
  3045. #endif
  3046.   cmxprintf (" MS Distributed Mail System %s\n",version);
  3047. }
  3048.  
  3049. /* Message reading subroutines */
  3050.  
  3051.  
  3052. /* Answer a message
  3053.  * Accepts: message number
  3054.  *        answer to all flag
  3055.  */
  3056.  
  3057. void answer_message (int msgno,int allflag)
  3058. {
  3059.   char tmp[TMPLEN];
  3060.   BODY *body;
  3061.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  3062.   ENVELOPE *msg;
  3063.   if (env && env->reply_to) {    /* if reply address */
  3064.     msg = send_init ();        /* get a message block */
  3065.                 /* copy reply-to */
  3066.     msg->to = copy_adr (env->reply_to,NIL);
  3067.     if (allflag) {        /* user asked for ALL reply */
  3068.       msg->cc = copy_adr (env->to,NIL);
  3069.       copy_adr (env->cc,msg->cc);
  3070.       msg->bcc = copy_adr (env->bcc,NIL);
  3071.     }
  3072.     if (env->subject) {        /* use subject in reply */
  3073.       strncpy (tmp,env->subject,3);
  3074.       tmp[3] ='\0';        /* tie off copy of first 3 chars */
  3075.       ucase (tmp);        /* make the whole thing uppercase */
  3076.                 /* a "re:" already there? */
  3077.       if (strcmp (tmp,"RE:")) sprintf (tmp,"re: %s",env->subject);
  3078.       else sprintf (tmp,"%s",env->subject);
  3079.     }
  3080.     else sprintf (tmp,"(response to message of %s)",env->date);
  3081.     msg->subject = cpystr (tmp);/* copy desired subject */
  3082.                 /* in-reply-to is our message-id if exists */
  3083.     if (env->message_id) sprintf (tmp,"%s",env->message_id);
  3084.     else {            /* build one from other info */
  3085.       if (env->from->personal)
  3086.     sprintf (tmp,"Message of %s from %s",env->date,
  3087.          env->from->personal);
  3088.       else sprintf (tmp,"Message of %s from %s@%s",env->date,
  3089.             env->from->mailbox,env->from->host);
  3090.     }
  3091.                 /* copy in-reply-to */
  3092.     msg->in_reply_to = cpystr (tmp);
  3093.     if (body = send_text ()) {    /* get text, send message */
  3094.       quitted = NIL;
  3095.       send_level (msg,body);
  3096.       mail_free_body (&body);
  3097.       if (!quitted) {        /* unless quitted */
  3098.     sprintf (tmp,"%d",msgno);
  3099.     mail_setflag (stream,tmp,"\\Answered");
  3100.       }
  3101.     }
  3102.     mail_free_envelope (&msg);    /* flush the message */
  3103.   }
  3104. }
  3105.  
  3106. /* Output header for message
  3107.  * Accepts: file to output to
  3108.  *        message number
  3109.  */
  3110.  
  3111. void header_message (FILE *file,long msgno)
  3112. {
  3113.   long i;
  3114.   char tmp[TMPLEN],frm[FROMLEN+1];
  3115.   char flgs[5];
  3116.   char date[7];
  3117.   MESSAGECACHE *c = mail_elt (stream,msgno);
  3118.   flgs[4] = (date[6] = '\0');    /* tie off constant width strings */
  3119.                 /* make sure it's in the cache */
  3120.   mail_fetchstructure (stream,msgno,NIL);
  3121.                 /* get system flags */
  3122.                 /* If recent, then either "recent" or "new"
  3123.                    otherwise, either "seen" or "unseen" */
  3124.   flgs[0] = c->recent ? (c->seen ? 'R' : 'N') : (c->seen ? ' ' : 'U');
  3125.   flgs[1] = c->flagged ? 'F' : ' ';
  3126.   flgs[2] = c->answered ? 'A' : ' ';
  3127.   flgs[3] = c->deleted ? 'D' : ' ';
  3128.                 /* only use "dd-mmm" from date */
  3129.   if (c->day) sprintf (date,"%2d-%s",c->day,months[c->month - 1]);
  3130.   else strncpy (date,"dd-mmm",6);
  3131.   if (i = c->user_flags) {    /* first stash user flags into tmp */
  3132.     tmp[0] = '{';        /* open brace for keywords */
  3133.     tmp[1] = '\0';        /* tie off so strcat starts off right */
  3134.     while (i) {
  3135.                 /* output this keyword */
  3136.       strcat (tmp,stream->user_flags[find_rightmost_bit (&i)]);
  3137.                 /* followed by spaces until done */
  3138.       if (i) strcat (tmp," ");
  3139.     }
  3140.     strcat (tmp,"} ");        /* close brace and space before subject */
  3141.   }
  3142.   else tmp[0] = '\0';
  3143.   mail_fetchfrom (frm,stream,msgno,FROMLEN);
  3144.                 /* now append the subject */
  3145.   mail_fetchsubject (tmp + strlen (tmp),stream,msgno,SUBJECTLEN);
  3146.   tmp[SUBJECTLEN] = '\0';    /* and trim it to the subject length */
  3147.                 /* output what we got */
  3148.   fprintf (file,"%s%4d) %s %s %s (%d chars)\n",flgs,c->msgno,date,frm,
  3149.        tmp,c->rfc822_size);
  3150. }
  3151.  
  3152. /* Literal type a message
  3153.  * Accepts: file to output to
  3154.  *        message number
  3155.  */
  3156.  
  3157. void literal_type_message (FILE *file,long msgno)
  3158. {
  3159.   char c;
  3160.   char *t;
  3161.   char *hdr = cpystr (mail_fetchheader (stream,msgno));
  3162.   char *text = mail_fetchtext (stream,msgno);
  3163.                 /* output the poop */
  3164.   fprintf (file,"Message %d of %d (%d chars)\n",msgno,nmsgs,
  3165.        strlen (hdr) + strlen (text));
  3166. #if unix
  3167.   for (t = hdr; c = *t++;) if (c != '\r') putc (c,file);
  3168.   while (c = *text++) if (c != '\r') putc (c,file);
  3169. #else
  3170.   fputs (hdr,file);
  3171.   fputs (text,file);
  3172. #endif
  3173.   putc ('\n',file);
  3174.   fs_give ((void **) &hdr);
  3175. }
  3176.  
  3177. /* Remail a message
  3178.  * Accepts: message number
  3179.  *        remail address list
  3180.  */
  3181.  
  3182. void remail_message (int msgno,ADDRESS *adr)
  3183. {
  3184.   ENVELOPE *msg = send_init ();
  3185.   BODY *body = mail_newbody ();
  3186.   msg->to = adr;        /* set to-list */
  3187.                 /* get header */
  3188.   msg->remail = cpystr (mail_fetchheader (stream,msgno));
  3189.                 /* get body of message */
  3190.   body->contents.text = (unsigned char *) cpystr(mail_fetchtext(stream,msgno));
  3191.   send_message (msg,body);    /* send off the message */
  3192.                 /* if lost, enter send-level */
  3193.   if (!done) send_level (msg,body);
  3194.   else done = NIL;        /* restore prior done state */
  3195.   msg->to = NIL;        /* be sure the address isn't nuked */
  3196.   mail_free_envelope (&msg);    /* flush the message */
  3197.   mail_free_body (&body);
  3198. }
  3199.  
  3200. /* Type a message
  3201.  * Accepts: file to output to
  3202.  *        message number
  3203.  */
  3204.  
  3205. void type_message (FILE *file,long msgno)
  3206. {
  3207.   int i,j;
  3208.   char *text;
  3209.   char c;
  3210.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  3211.                 /* make sure we get some text */
  3212.   if (text = mail_fetchtext (stream,msgno)) {
  3213.                 /* output the poop */
  3214.     fprintf (file,"Message %d of %d (%d chars)\n",msgno,nmsgs,strlen (text));
  3215.     if (env) {            /* make sure we have an envelope */
  3216.                 /* output envelope */
  3217.       if (env->date) fprintf (file,"Date: %s\n",env->date);
  3218.       type_address (file,"From",env->from);
  3219.       if (env->subject) fprintf (file,"Subject: %s\n",env->subject);
  3220.       type_address (file,"To",env->to);
  3221.       type_address (file,"cc",env->cc);
  3222.       type_address (file,"bcc",env->bcc);
  3223.     }
  3224.     putc ('\n',file);        /* output message text */
  3225. #if unix
  3226.     while (c = *text++) if (c != '\r') putc (c,file);
  3227. #else
  3228.     fputs (text,file);
  3229. #endif
  3230.     putc ('\n',file);
  3231.   }
  3232. }
  3233.  
  3234. /* Type an address
  3235.  * Accepts: file to output to
  3236.  *        tag string to start with
  3237.  *        address to output
  3238.  */
  3239.  
  3240. void type_address (FILE *file,char *tag,ADDRESS *adr)
  3241. {
  3242.   char c,tmp[8196];
  3243.   char *s = tmp;
  3244.   *s = '\0';
  3245.   rfc822_address_line (&s,tag,NIL,adr);
  3246.   for (s = tmp; c = *s++;) if (c != '\r') putc (c,file);
  3247. }
  3248.  
  3249.  
  3250. /* Type a string or string del string
  3251.  * Accepts: file to output to
  3252.  *        pointer current line position
  3253.  *        first string to output
  3254.  *        optional delimiter
  3255.  *        second string to output
  3256.  */
  3257.  
  3258. #define MAXLINE 78
  3259. void type_string (FILE *file,int *pos,char *str1,char chr,char *str2)
  3260. {
  3261.   int i;
  3262.   i = strlen (str1) + 2;    /* count up space, length of string, comma */
  3263.   if (chr) i++;            /* bump if delimiter and second string */
  3264.   if (str2) i += strlen (str2);
  3265.   if ((*pos += i) > MAXLINE) {    /* line too long? */
  3266.     fprintf (file,"\n ");    /* yes, start new line */
  3267.     *pos = i + 2;        /* reset position */
  3268.   }
  3269.   fprintf (file," %s",str1);    /* output first string */
  3270.   if (chr) fputc (chr,file);    /* delimiter and second string */
  3271.   if (str2) fprintf (file,"%s",str2);
  3272. }
  3273.  
  3274. /* Message sending subroutines */
  3275.  
  3276.  
  3277. /* Create a message composition structure
  3278.  * Returns: message structure
  3279.  */
  3280.  
  3281. ENVELOPE *send_init ()
  3282. {
  3283.   char tmp[TMPLEN];
  3284.   ENVELOPE *msg = mail_newenvelope ();
  3285.   rfc822_date (tmp);
  3286.   msg->date = cpystr (tmp);
  3287.   if (personal) sprintf (tmp,"%s <%s@%s>",personal,curusr,curhst);
  3288.   else sprintf (tmp,"%s@%s",curusr,curhst);
  3289.   rfc822_parse_adrlist (&msg->from,tmp,curhst);
  3290.   sprintf (tmp,"%s@%s",curusr,curhst);
  3291.   rfc822_parse_adrlist (&msg->return_path,tmp,curhst);
  3292.   sprintf (tmp,"<MS-C.%d.%d.%s@%s>",time (0),rand (),curusr,curhst);
  3293.   msg->message_id = cpystr (tmp);
  3294.   return (msg);
  3295. }
  3296.  
  3297. /* Copy address list
  3298.  * Accepts: MAP address list
  3299.  *        optional MTP address list to append to
  3300.  * Returns: MTP address list
  3301.  */
  3302.  
  3303. ADDRESS *copy_adr (ADDRESS *adr,ADDRESS *ret)
  3304. {
  3305.   ADDRESS *dadr;        /* current destination */
  3306.   ADDRESS *prev = ret;        /* previous destination */
  3307.   ADDRESS *tadr;
  3308.                 /* run down previous list until the end */
  3309.   if (prev) while (prev->next) prev = prev->next;
  3310.   while (adr) {            /* loop while there's still an MAP adr */
  3311.     if (adr->host) {        /* ignore group stuff */
  3312.       dadr = mail_newaddr ();    /* instantiate a new address */
  3313.       if (!ret) ret = dadr;    /* note return */
  3314.                 /* tie on to the end of any previous */
  3315.       if (prev) prev->next = dadr;
  3316.       dadr->personal = cpystr (adr->personal);
  3317.       dadr->adl = cpystr (adr->adl);
  3318.       dadr->mailbox = cpystr (adr->mailbox);
  3319.       dadr->host = cpystr (adr->host);
  3320.       prev = dadr;        /* this is now the previous */
  3321.     }
  3322.     adr = adr->next;        /* go to next address in list */
  3323.   }
  3324.   return (ret);            /* return the MTP address list */
  3325. }
  3326.  
  3327. /* Remove recipient from an address list
  3328.  * Accepts: list
  3329.  *        text of recipient
  3330.  * Returns: list (possibly zapped to NIL)
  3331.  */
  3332.  
  3333. ADDRESS *remove_adr (ADDRESS *adr,char *text)
  3334. {
  3335.   ADDRESS *ret = adr;
  3336.   ADDRESS *prev = NIL;
  3337.   while (adr) {            /* run down the list looking for this guy */
  3338.                 /* is this one we want to punt? */
  3339.     if (!strcmp (adr->mailbox,text)) {
  3340.                 /* yes, unlink this from the list */
  3341.       if (prev) prev->next = adr->next;
  3342.       else ret = adr->next;
  3343.       adr->next = NIL;        /* unlink list from this */
  3344.       mail_free_address (&adr);    /* now flush it */
  3345.                 /* get the next in line */
  3346.       adr = prev ? prev->next : ret;
  3347.     }
  3348.     else {
  3349.       prev = adr;        /* remember the previous */
  3350.       adr = adr->next;        /* try the next in line */
  3351.     }
  3352.   }
  3353.   return (ret);            /* all done */
  3354. }
  3355.  
  3356. /* Prompt for and get message text
  3357.  * Returns: message body
  3358.  */
  3359.  
  3360. BODY *send_text ()
  3361. {
  3362.   BODY *body;
  3363.   pval parseval;
  3364.   fdb *used;
  3365.   static para_data pd = {NIL,NIL};
  3366.   static fdb parafdb = {_CMPARA,NIL,NIL,NIL,NIL,NIL,NIL};
  3367.   parafdb._cmdat = (pdat) &pd;
  3368.   cmseter ();            /* set error trap */
  3369.                 /* prompt for text */
  3370.   cmxprintf (" Message (CTRL/D to send,\n\
  3371.   Use CTRL/B to insert a file, CTRL/E to enter editor, CTRL/K to redisplay\n\
  3372.   message, CTRL/L to clear screen and redisplay, CTRL/N to abort.):\n");
  3373.   cmsetrp ();            /* set reparse trap */
  3374.                 /* get the text */
  3375.   parse (¶fdb,&parseval,&used);
  3376.                 /* copy and return text if got any */
  3377.   if (parseval._pvpara == NIL) {/* aborted? */
  3378.     cmxprintf ("?Aborted\n");    /* yes, punt */
  3379.     return NIL;
  3380.   }
  3381.   cmxprintf ("^D\n");        /* give confirmation of the end */
  3382.   body = mail_newbody ();    /* make a new body */
  3383.   body->contents.text = (void *) cpystr (parseval._pvpara);
  3384.                 /* ISO-2022 stuff inside? */
  3385.   if (strstr ((char *) body->contents.text,"\033$")) {
  3386.     body->parameter = mail_newbody_parameter ();
  3387.     body->parameter->attribute = cpystr ("charset");
  3388.     body->parameter->value = cpystr ("ISO-2022-JP");
  3389.   }
  3390.   return body;
  3391. }
  3392.  
  3393. /* Send the message off
  3394.  * Accepts: message
  3395.  */
  3396.  
  3397. void send_message (ENVELOPE *msg,BODY *body)
  3398. {
  3399.   char tmp[TMPLEN];
  3400.   SMTPSTREAM *stream;
  3401.   done = T;            /* assume all is well */
  3402.   if (msg->to || msg->cc || msg->bcc) {
  3403.                 /* get MTP connection */
  3404.     if (!(stream = smtp_open (hostlist,debug))) {
  3405.       cmerr ("Can't open connection to any server");
  3406.       done = NIL;
  3407.     }
  3408.     else {            /* deliver message */
  3409.       if (!smtp_mail (stream,"MAIL",msg,body)) {
  3410.     sprintf (tmp,"Mailing failed - %s",stream->reply);
  3411.     cmerr (tmp);
  3412.     done = NIL;
  3413.       }
  3414.       smtp_close (stream);    /* close SMTP */
  3415.     }
  3416.   }
  3417.   if (done && msg->newsgroups) {/* want posting? */
  3418.                 /* get NNTP connection */
  3419.     if (!(stream = nntp_open (newslist,debug))) {
  3420.       cmerr ("Can't open connection to any news server");
  3421.       done = NIL;
  3422.     }
  3423.     else {            /* deliver message */
  3424.       if (!nntp_mail (stream,msg,body)) {
  3425.     sprintf (tmp,"Posting failed - %s",stream->reply);
  3426.     cmerr (tmp);
  3427.     done = NIL;
  3428.       }
  3429.       smtp_close (stream);    /* close NNTP */
  3430.     }
  3431.   }
  3432. }
  3433.  
  3434. /* Auxillary command parsing routines */
  3435.  
  3436.  
  3437. /* Get ON or OFF
  3438.  * Returns: NIL if OFF, T or NIL
  3439.  */
  3440.  
  3441. static keywrd onfcmds[] = {
  3442.   {"OFF",    NIL,    (keyval) NIL},
  3443.   {"ON",    NIL,    (keyval) T},
  3444. };
  3445. static keytab onftab = {(sizeof (onfcmds)/sizeof (keywrd)),onfcmds};
  3446.  
  3447. int onoff ()
  3448. {
  3449.   pval parseval;
  3450.   fdb *used;
  3451.   static fdb cmdfdb = {_CMKEY,NIL,NIL,(pdat) &(onftab),"Option state, ","ON",
  3452.              NIL};
  3453.                 /* parse command */
  3454.   parse (&cmdfdb,&parseval,&used);
  3455.   return (parseval._pvint);    /* return the state */
  3456. }
  3457.  
  3458.  
  3459. /* Report an error
  3460.  * Accepts: error
  3461.  */
  3462.  
  3463. void cmerr (char *string)
  3464. {
  3465.   cmflush (cmcsb._cmij);    /* flush waiting input */
  3466.                 /* newline if necessary */
  3467.   if (cmcpos() != 0) cmnl (cmcsb._cmej);
  3468.   cmcsb._cmcol = 0;        /* make sure our counter agrees */
  3469.   cmputc ('?',cmcsb._cmej);    /* start with question mark */
  3470.   cmputs (string,cmcsb._cmej);    /* then the error string */
  3471.   cmnl (cmcsb._cmej);        /* tie off with newline */
  3472. }
  3473.  
  3474. /* Co-routines from c-client */
  3475.  
  3476.  
  3477. /* Message matches a search
  3478.  * Accepts: MAIL stream
  3479.  *        message number
  3480.  */
  3481.  
  3482. void mm_searched (MAILSTREAM *s,long msgno)
  3483. {
  3484.                 /* don't need to do anything special here */
  3485. }
  3486.  
  3487.  
  3488. /* Message exists (i.e. there are that many messages in the mailbox)
  3489.  * Accepts: MAIL stream
  3490.  *        message number
  3491.  */
  3492.  
  3493. void mm_exists (MAILSTREAM *s,long number)
  3494. {
  3495.   int delta;
  3496.   if (s != stream) return;
  3497.   if ((delta = number-nmsgs) < 0)
  3498.     cmxprintf ("%%Mailbox shrunk from %d to %d!!",nmsgs,number);
  3499.   if (nmsgs) switch (delta) {    /* no output if first time */
  3500.   case 0:            /* no new messages */
  3501.     break;
  3502.   case 1:
  3503.     cmxprintf ("[There is 1 new message]\n");
  3504.     break;
  3505.   default:
  3506.     cmxprintf ("[There are %d new messages]\n",delta);
  3507.     break;
  3508.   }
  3509.   nmsgs = number;        /* update local copy of nmsgs */
  3510. }
  3511.  
  3512.  
  3513. /* Message expunged
  3514.  * Accepts: MAIL stream
  3515.  *        message number
  3516.  */
  3517.  
  3518. void mm_expunged (MAILSTREAM *s,long number)
  3519. {
  3520.   if (s == stream) nmsgs--;    /* decrement number of messages */
  3521. }
  3522.  
  3523. /* Mailbox found
  3524.  * Accepts: Mailbox name
  3525.  */
  3526.  
  3527. void mm_mailbox (char *string)
  3528. {
  3529.   int i,j;
  3530.   for (i = 0;i < mbxtab._ktcnt && (j = strcmp (mbx[i]._kwkwd,string)) <= 0;i++)
  3531.     if (!j) return;        /* done if string already present */
  3532.   if (i == MAXMAILBOXES) cmxprintf ("%%Mailbox limit exceeded -- %s\n",string);
  3533.   else {            /* add new mailbox name */
  3534.     for (j = mbxtab._ktcnt++; i < j; j--) mbx[j] = mbx[j - 1];
  3535.     mbx[i]._kwkwd = cpystr (string);
  3536.     mbx[i]._kwflg = KEY_EMO;
  3537.     mbx[i]._kwval = NIL;
  3538.   }
  3539. }
  3540.  
  3541.  
  3542. /* BBoard found
  3543.  * Accepts: BBoard name
  3544.  */
  3545.  
  3546. void mm_bboard (char *string)
  3547. {
  3548.   int i,j;
  3549.   for (i = 0;i < bbdtab._ktcnt && (j = strcmp (bbd[i]._kwkwd,string)) <= 0;i++)
  3550.     if (!j) return;        /* done if string already present */
  3551.   if (i == MAXMAILBOXES) cmxprintf ("%%Mailbox limit exceeded -- %s\n",string);
  3552.   else {            /* add new mailbox name */
  3553.     for (j = bbdtab._ktcnt++; i < j; j--) bbd[j] = bbd[j - 1];
  3554.     bbd[i]._kwkwd = cpystr (string);
  3555.     bbd[i]._kwflg = KEY_EMO;
  3556.     bbd[i]._kwval = NIL;
  3557.   }
  3558. }
  3559.  
  3560. /* Notification event
  3561.  * Accepts: MAIL stream
  3562.  *        string to log
  3563.  *        error flag
  3564.  */
  3565.  
  3566. void mm_notify (MAILSTREAM *s,char *string,long errflg)
  3567. {
  3568.   mm_log (string,errflg);    /* just do mm_log action */
  3569. }
  3570.  
  3571.  
  3572. /* Log an event for the user to see
  3573.  * Accepts: string to log
  3574.  *        error flag
  3575.  */
  3576.  
  3577. void mm_log (char *string,long errflg)
  3578. {
  3579.   switch (errflg) {  
  3580.   case NIL:            /* no error */
  3581.     cmxprintf ("[%s]\n",string);
  3582.     break;
  3583.   case PARSE:            /* parsing problem */
  3584.   case WARN:            /* warning */
  3585.     cmxprintf ("%%%s\n",string);
  3586.     break;
  3587.   case ERROR:            /* error */
  3588.   default:
  3589.     cmxprintf ("?%s\n",string);
  3590.     break;
  3591.   }
  3592. }
  3593.  
  3594.  
  3595. /* Log an event to debugging telemetry
  3596.  * Accepts: string to log
  3597.  */
  3598.  
  3599. void mm_dlog (char *string)
  3600. {
  3601.   fprintf (stderr,"%s\n",string);
  3602. }
  3603.  
  3604. /* Get user name and password for this host
  3605.  * Accepts: host name
  3606.  *        where to return user name
  3607.  *        where to return password
  3608.  *        trial count
  3609.  */
  3610.  
  3611. void mm_login (char *host,char *username,char *password,long trial)
  3612. {
  3613.   char tmp[TMPLEN];
  3614.   pval parseval;
  3615.   fdb *used;
  3616.   static brktab usrbrk = {
  3617.     {                /* 1st char break array */
  3618.       0xff,0xff,0xff,0xff,0xff,0xfa,0x00,0x15,
  3619.       0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1b
  3620.     },
  3621.     {                /* subsequent char break array */
  3622.                 /* same as above, plus dots */
  3623.       0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x15,
  3624.       0x80,0x00,0x00,0x1f,0x80,0x00,0x00,0x1f
  3625.     }
  3626.   };
  3627.   static brktab pswbrk = {
  3628.     {                /* 1st char break array */
  3629.       0x00,0xa4,0x25,0x10,0x00,0x00,0x00,0x00,
  3630.       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01
  3631.     },
  3632.     {                /* subsequent char break array */
  3633.       0x00,0xa4,0x25,0x10,0x00,0x00,0x00,0x00,
  3634.       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01
  3635.     }
  3636.   };
  3637.   static fdb namfdb = {_CMFLD,CM_SDH,NIL,NIL,"remote user name",NIL,&usrbrk};
  3638.   static fdb pasfdb = {_CMFLD,CM_SDH,NIL,NIL,"remote password",NIL,&pswbrk};
  3639.   namfdb._cmdef = curusr;
  3640.   if (curhst) fs_give ((void **) &curhst);
  3641.   curhst = cpystr (host);
  3642.   sprintf (tmp,"{%s} username: ",host);
  3643.   cmseter ();            /* set error trap */
  3644.   prompt (tmp);            /* prompt for and get user name */
  3645.   cmsetrp ();            /* set reparse trap */
  3646.   parse (&namfdb,&parseval,&used);
  3647.   strcpy (username,atmbuf);
  3648.   confirm ();
  3649.   if (curusr) fs_give ((void **) &curusr);
  3650.   curusr = cpystr (username);
  3651.   cmseter ();            /* set error trap */
  3652.   prompt ("Password: ");
  3653.   cmsetrp ();            /* set reparse trap */
  3654.   cmecho (NIL);            /* nuke echoing */
  3655.   parse (&pasfdb,&parseval,&used);
  3656.   strcpy (password,atmbuf);
  3657.   confirm ();
  3658.   cmxputc ('\n');        /* echo newline */
  3659.   cmecho (T);            /* re-enable echoing */
  3660.   cmnohist ();            /* clean buffers */
  3661. }
  3662.  
  3663. /* About to enter critical code
  3664.  * Accepts: stream
  3665.  */
  3666.  
  3667. void mm_critical (MAILSTREAM *s)
  3668. {
  3669.   critical = T;            /* note in critical code */
  3670. }
  3671.  
  3672.  
  3673. /* About to exit critical code
  3674.  * Accepts: stream
  3675.  */
  3676.  
  3677. void mm_nocritical (MAILSTREAM *s)
  3678. {
  3679.   critical = NIL;        /* note not in critical code */
  3680. }
  3681.  
  3682.  
  3683. /* Disk error found
  3684.  * Accepts: stream
  3685.  *        system error code
  3686.  *        flag indicating that mailbox may be clobbered
  3687.  * Returns: T if user wants to abort
  3688.  */
  3689.  
  3690. long mm_diskerror (MAILSTREAM *s,long errcode,long serious)
  3691. {
  3692.   if (serious) cmxprintf ("[Warning: mailbox on disk is probably damaged.]\n");
  3693.   cmxprintf ("Will retry if you continue MS.\n");
  3694.   kill (getpid (),SIGSTOP);
  3695.   cmxprintf ("Retrying...\n");
  3696.   return NIL;
  3697. }
  3698.  
  3699.  
  3700. /* Log a fatal error event
  3701.  * Accepts: string to log
  3702.  */
  3703.  
  3704. void mm_fatal (char *string)
  3705. {
  3706.   mm_log (string,ERROR);    /* shouldn't happen normally */
  3707. }
  3708.